递归可变函数模板
Recursive variadic function template
我想编写一个 class 方法,该方法采用模板参数包,但参数为零,"iterate" 类型:
struct Bar {
template <typename T, typename... Ts>
void foo() {
// something with T that involves Bar's members
foo<Ts...>();
}
};
实现这个的首选方法是什么?
我喜欢重载函数和使用类型列表:
#include <iostream>
#include <typeinfo>
template <typename ...Ts> struct typelist { };
void foo_impl(typelist<> )
{
// we are finished
}
template <typename T, typename ...Ts>
void foo_impl(typelist<T, Ts...> )
{
std::cout << typeid(T).name() << ", ";
foo_impl(typelist<Ts...>{});
}
template <typename ...Ts>
void foo()
{
std::cout << "called with <";
foo_impl(typelist<Ts...>{});
std::cout << ">" << std::endl;
}
int main()
{
foo<int, char, float>();
}
您可以使用以下内容:
struct Bar {
template <typename... Ts>
void foo() {
int dummy[] = {0 /*Manage case where Ts is empty*/,
(bar<Ts>(), void() /* To avoid overload `operator,` */, 0)...};
(void) dummy; // suppress warning for unused variable.
}
template <typename T>
void bar()
{
// something with T that involves Bar's members
}
};
在C++17中,可以用折叠表达式简化:
struct Bar {
template <typename... Ts>
void foo() {
(static_cast<void>(bar<Ts>()), ...);
}
template <typename T>
void bar()
{
// something with T that involves Bar's members
}
};
template<class...Fs>
void do_in_order(Fs&&...fs) {
int _[]={0, ( std::forward<Fs>(fs)(), void(), 0 )...};
(void)_;
}
隐藏了按从左到右的顺序执行一组函数对象所需的语法。
然后:
struct Bar {
template <class... Ts>
void foo() {
do_in_order([&]{
using T = Ts;
// code
}...);
}
};
并且在符合标准的编译器中,我们将从左到右 运行 // code
和 T
每种类型。
请注意,某些自称是 C++11 编译器的编译器可能无法编译上述内容。
这种技术的优势在于它隐藏了具有清晰名称的函数中令人讨厌的 "expand and evaluate templates" 代码。你写 do_in_order
一次,它通常足以满足几乎每次使用数组扩展技巧的需要。
使用这种深奥的语法而不是 "more simple" 递归解决方案有两个重要原因。
首先,它使优化器的工作变得更容易。优化器有时会在一堆递归调用后放弃。
其次,传统递归函数的函数签名的长度总和以 O(n^2) 的速度增长。如果您使用辅助类型,名称的总长度也是 O(n^2)。除非您小心,否则这会导致编译时间、link 时间和二进制大小膨胀。
在 C++1z 中,一些 "fold" 语法的计划可能会使上述的深奥部分变得不那么深奥。
我想编写一个 class 方法,该方法采用模板参数包,但参数为零,"iterate" 类型:
struct Bar {
template <typename T, typename... Ts>
void foo() {
// something with T that involves Bar's members
foo<Ts...>();
}
};
实现这个的首选方法是什么?
我喜欢重载函数和使用类型列表:
#include <iostream>
#include <typeinfo>
template <typename ...Ts> struct typelist { };
void foo_impl(typelist<> )
{
// we are finished
}
template <typename T, typename ...Ts>
void foo_impl(typelist<T, Ts...> )
{
std::cout << typeid(T).name() << ", ";
foo_impl(typelist<Ts...>{});
}
template <typename ...Ts>
void foo()
{
std::cout << "called with <";
foo_impl(typelist<Ts...>{});
std::cout << ">" << std::endl;
}
int main()
{
foo<int, char, float>();
}
您可以使用以下内容:
struct Bar {
template <typename... Ts>
void foo() {
int dummy[] = {0 /*Manage case where Ts is empty*/,
(bar<Ts>(), void() /* To avoid overload `operator,` */, 0)...};
(void) dummy; // suppress warning for unused variable.
}
template <typename T>
void bar()
{
// something with T that involves Bar's members
}
};
在C++17中,可以用折叠表达式简化:
struct Bar {
template <typename... Ts>
void foo() {
(static_cast<void>(bar<Ts>()), ...);
}
template <typename T>
void bar()
{
// something with T that involves Bar's members
}
};
template<class...Fs>
void do_in_order(Fs&&...fs) {
int _[]={0, ( std::forward<Fs>(fs)(), void(), 0 )...};
(void)_;
}
隐藏了按从左到右的顺序执行一组函数对象所需的语法。
然后:
struct Bar {
template <class... Ts>
void foo() {
do_in_order([&]{
using T = Ts;
// code
}...);
}
};
并且在符合标准的编译器中,我们将从左到右 运行 // code
和 T
每种类型。
请注意,某些自称是 C++11 编译器的编译器可能无法编译上述内容。
这种技术的优势在于它隐藏了具有清晰名称的函数中令人讨厌的 "expand and evaluate templates" 代码。你写 do_in_order
一次,它通常足以满足几乎每次使用数组扩展技巧的需要。
使用这种深奥的语法而不是 "more simple" 递归解决方案有两个重要原因。
首先,它使优化器的工作变得更容易。优化器有时会在一堆递归调用后放弃。
其次,传统递归函数的函数签名的长度总和以 O(n^2) 的速度增长。如果您使用辅助类型,名称的总长度也是 O(n^2)。除非您小心,否则这会导致编译时间、link 时间和二进制大小膨胀。
在 C++1z 中,一些 "fold" 语法的计划可能会使上述的深奥部分变得不那么深奥。