对可变参数模板类型列表的每种类型进行操作的函数
Function operating on each type of a variadic template typelist
我定义了这样一个类型列表:
template <typename ... Types> struct typelist {};
using my_list = typelist<int, double, bool, float>;
现在我有了一个函数模板,例如
template<typename T>
void foo() {
std::cout << typeid(T).name() << std::endl;
}
并想为类型列表中的每个类型调用它:
foo<int>();
foo<double>();
foo<bool>();
foo<float>();
我试图找到一种递归方法来解决这个问题,但我无法为所需的 foo 函数定义正确的、可能是嵌套的可变参数模板。对于这个问题的巧妙解决方案,您有任何提示吗?
template<class... Types> auto foo_foreach(typelist<Types...>) {
return (foo<Types>(), ...);
}
int main() {
foo_foreach(my_list{});
}
对于真正的老派,好吧,使用您之前尝试过的模板递归:
void foo_foreach(typelist<>) {}
template<class Head, class... Tail> void foo_foreach(typelist<Head, Tail...>);
template<class Head, class... Tail> void foo_foreach(typelist<Head, Tail...>) {
foo<Head>();
foo_foreach(typelist<Tail...>{});
}
这是一个 c++20 答案,仅使用 lambda 模板。
template <typename... Ts, typename C>
constexpr void for_types(C&& c) {
(c.template operator()<Ts>(), ...);
}
for_types<int, float, char>([]<typename T>()
{
std::cout << typeid(T).name() << std::endl;
});
如果你需要它与 C++14 一起工作,那么你可以使用 initializer_list
技巧来避免 C++17 折叠表达式。这有望比递归方法更便宜:
template<class... Ts> void foo_foreach(typelist<Ts...>) {
(void) std::initializer_list<int>{(foo<Ts>(), 0)...};
}
int main() {
foo_foreach(my_list{});
}
神马示例:https://godbolt.org/z/o91Th466s
initializer_list
技巧在 https://blog.tartanllama.xyz/exploding-tuples-fold-expressions/
有很好的解释
来自那篇文章:
[...] parameter packs can only be expanded in contexts which expect a syntactic list, such as initializers and function call arguments. You can’t just expand them bare in a function body. In C++17, this problem has a nice solution, but prior to that we need to use some pretty horrible hacks. [...] one possibility [...] uses std::initializer_list
to create a context in which the parameter pack can be expanded.
The trick is the , 0
inside the initializer_list
initializer, which evaluates the function call, and uses 0 as the initializer value.
我定义了这样一个类型列表:
template <typename ... Types> struct typelist {};
using my_list = typelist<int, double, bool, float>;
现在我有了一个函数模板,例如
template<typename T>
void foo() {
std::cout << typeid(T).name() << std::endl;
}
并想为类型列表中的每个类型调用它:
foo<int>();
foo<double>();
foo<bool>();
foo<float>();
我试图找到一种递归方法来解决这个问题,但我无法为所需的 foo 函数定义正确的、可能是嵌套的可变参数模板。对于这个问题的巧妙解决方案,您有任何提示吗?
template<class... Types> auto foo_foreach(typelist<Types...>) {
return (foo<Types>(), ...);
}
int main() {
foo_foreach(my_list{});
}
对于真正的老派,好吧,使用您之前尝试过的模板递归:
void foo_foreach(typelist<>) {}
template<class Head, class... Tail> void foo_foreach(typelist<Head, Tail...>);
template<class Head, class... Tail> void foo_foreach(typelist<Head, Tail...>) {
foo<Head>();
foo_foreach(typelist<Tail...>{});
}
这是一个 c++20 答案,仅使用 lambda 模板。
template <typename... Ts, typename C>
constexpr void for_types(C&& c) {
(c.template operator()<Ts>(), ...);
}
for_types<int, float, char>([]<typename T>()
{
std::cout << typeid(T).name() << std::endl;
});
如果你需要它与 C++14 一起工作,那么你可以使用 initializer_list
技巧来避免 C++17 折叠表达式。这有望比递归方法更便宜:
template<class... Ts> void foo_foreach(typelist<Ts...>) {
(void) std::initializer_list<int>{(foo<Ts>(), 0)...};
}
int main() {
foo_foreach(my_list{});
}
神马示例:https://godbolt.org/z/o91Th466s
initializer_list
技巧在 https://blog.tartanllama.xyz/exploding-tuples-fold-expressions/
来自那篇文章:
[...] parameter packs can only be expanded in contexts which expect a syntactic list, such as initializers and function call arguments. You can’t just expand them bare in a function body. In C++17, this problem has a nice solution, but prior to that we need to use some pretty horrible hacks. [...] one possibility [...] uses
std::initializer_list
to create a context in which the parameter pack can be expanded.The trick is the
, 0
inside theinitializer_list
initializer, which evaluates the function call, and uses 0 as the initializer value.