提问人:Juan Gonzalez Burgos 提问时间:8/11/2023 最后编辑:Juan Gonzalez Burgos 更新时间:8/11/2023 访问量:83
有没有办法在 C++ 中使用可变参数模板检索类型的内部类型?
Is there a way to retrieve the inner types of a type using variadic templates in C++?
问:
假设我有一个使用可变参数模板的类型:
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");
}
答:
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_list
std::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
这也非常有用,谢谢
评论
using Specific = Outer<int, double, bool>;
<int, double, bool>
std::tuple
Specific1::Inner
看起来还不错,它只是缺少一个.您的代码太伪,无法看到您遇到的实际问题。请发布一个最小的可重复示例typename