有没有办法在 C++ 中使用可变参数模板检索类型的内部类型?

Is there a way to retrieve the inner types of a type using variadic templates in C++?

提问人:Juan Gonzalez Burgos 提问时间:8/11/2023 最后编辑:Juan Gonzalez Burgos 更新时间:8/11/2023 访问量:83

问:

假设我有一个使用可变参数模板的类型:

template <typename... Args>
struct Outer
{
  // using Inner = something that captures ...Args ???;
}

我该如何定义以便以后可以在一些模板化代码的其他地方使用它,例如:Inner

// ... some existing function accepting potentially other template arguments
template <typename... Args2>
foo(Args2 ...)
{
 // ...
};
// ... somewhere I define some specialized Outers, as examples but not limited to
using Specific1 = Outer<int, double, bool>;
using Specific2 = Outer<std::string>;
// ...
foo(Specific1::Inner... args)
// ...
foo(Specific2::Inner... args)

我主要对C++17感兴趣,但愿意学习,但它可以在任何其他版本的C++中完成。理想情况下,我想在不慢跑的情况下实现这一目标。std::tuple

一个最小的可重现示例:

template <typename... Args>
struct Outer
{
    using Cb = std::function<void(Args...)>;

    //using Inner = Args; // this one complains "parameter pack must be expanded in this context"
    //using Inner = Args...; // this one complains "parameter pack cannot be expanded in this context"

    template<typename CbIn>
    void store(CbIn&& cb)
    {
        mCb = std::forward<CbIn>(cb);
    }

    void call(Args... args) noexcept
    {
        mCb(args...);
    }

    // cb here accepts Args... and returns OtherOuter* (specialized with different Args)
    template<typename OtherOuter, typename CbIn>
    void foo(CbIn&& cb)
    {
        store([cb{ std::forward<CbIn>(cb)}](Args... args)
        {
            OtherOuter * other = cb(std::forward<Args>(args)...);
            other->store([](/*OtherOuter::Inner*/ ... otherArgs) // what to put here if not OtherOuter::Inner ???
            {
                // do something with otherArgs
                ([&]
                {
                    std::cout << "second " << otherArgs << std::endl;
                } (), ...);
            });
            std::cout << "first " << other->mFlag << std::endl;
        });
    }

    Cb mCb;
    bool mFlag = false;
};

并使用示例:

using OuterIntBool = Outer<int, bool>;
using OuterString = Outer<std::string>;
// works
{
    OuterIntBool outerIntBool;
    outerIntBool.store([](int i, bool b)
    {
        bool isValid = i > 0 && b;
        assert(isValid);
    });
    outerIntBool.call(1, true);
}
// does not work
{
    OuterIntBool outerIntBool;
    OuterString otherString;
    outerIntBool.foo<OuterString>([&otherString](int/* i*/, bool b)
    {
        otherString.mFlag = b;
        return &otherString;
    });
    outerIntBool.call(1, true);
    otherString.call("bar");
}
C++ 模板 variadic-templates 使用

评论

0赞 273K 8/11/2023
内在应该是什么?什么是内部类型?你从来没有解释过这一点。
0赞 Juan Gonzalez Burgos 8/11/2023
- 内在应该是什么?这实际上是一个问题——什么是内在类型?它们可以是任何东西,因为这些是可变模板参数。这只是一个示例,此特定示例中的内部类型是 。using Specific = Outer<int, double, bool>;<int, double, bool>
0赞 273K 8/11/2023
你在找吗?std::tuple
1赞 463035818_is_not_an_ai 8/11/2023
Specific1::Inner看起来还不错,它只是缺少一个.您的代码太伪,无法看到您遇到的实际问题。请发布一个最小的可重复示例typename
1赞 463035818_is_not_an_ai 8/11/2023
一个最小的可重现示例不是 1k 行代码。您不需要 1k 行代码来编写像您刚刚发布的那样的小示例。但是,该示例不应包含不相关的错误墙。它应该说明您的问题是关于一个问题的

答:

1赞 PidTuner 8/11/2023 #1

你试过吗?other->store([](auto&& ... otherArgs)

评论

0赞 Juan Gonzalez Burgos 8/11/2023
天哪,就是这样!谢谢。
3赞 Jarod42 8/11/2023 #2

如何“保存”可变参数模板参数?

直接的方法,就像我们对常规模板参数所做的那样是不可能的:

template <typename T>
struct s1
{
    using type = T; // OK
};

template <typename... Ts>
struct s2
{
    using types = Ts...; // KO
};

但是,它们是“保存”可变参数的几种方法:

  • 使用一些可变参数类型来保留它或您自己的type_liststd::tuple

    template <typename... Ts> struct type_list {};
    
    template <typename... Args>
    struct Outer
    {
        using type1s = std::tuple<Args...>;
        using type2s = type_list<Args...>;
    };
    
  • 一种解构方式:

    template <typename... Args>
    struct Outer
    {
        constexpr type_size = sizeof...(Args);
        template <std::size_t I>
        using type = std::tuple_element_t<I, std::tuple<Args...>>;
    };
    
  • 外部type_trait(具有上述任何结果)。所以解构方式的例子:

    template <typename T>
    constexpr std::size_t outer_size;
    
    template <typename... Ts>
    constexpr std::size_t outer_size<Outer<Ts...>> = sizeof...(Ts);
    
    template <std::size_t I, typename T>
    struct arg_element;
    
    template <std::size_t I, typename... Ts>
    struct arg_element<I, Outer<Ts...>> {
        using type = std::tuple_element_t<Is, std::tuple<Ts...>>;
    };
    

如何使用保存的参数?

std::index_sequence可能会有所帮助:

template <typename Outer>
void get_printer(const Outer&)
{
    []<std::size_t... Is>(std::index_sequence<Is...>){
        // typename Outer::template type<Is>... is not equivalent to the Ts...
        // std::tuple_element_t<Is, typename Outer::types>... for tuple case

        return [](const typename Outer::template type<Is>&... args){
            (std::cout << args), ...);
        };
    }(std::make_index_sequence<Outer::size_type>()); // tuple_size<Outer::types>


}

评论

0赞 Juan Gonzalez Burgos 8/14/2023
这也非常有用,谢谢