Recursive Variation Function Pattern

I want to write a class method that takes a package of template parameters, but null arguments and iterates over types:

struct Bar { template <typename T, typename... Ts> void foo() { // something with T that involves Bar members foo<Ts...>(); } }; 

What is the preferred way to implement this?

+7
c ++ c ++ 11 variadic-templates
source share
3 answers

You can use the following:

 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 members } }; 

In C ++ 17, it can be simplified using the Folding expression:

 struct Bar { template <typename... Ts> void foo() { (static_cast<void>(bar<Ts>()), ...); } template <typename T> void bar() { // something with T that involves Bar members } }; 
+3
source share
 template<class...Fs> void do_in_order(Fs&&...fs) { int _[]={0, ( std::forward<Fs>(fs)(), void(), 0 )...}; (void)_; } 

hides the syntax needed to execute a package of function objects in order from left to right.

Then:

 struct Bar { template <class... Ts> void foo() { do_in_order([&]{ using T = Ts; // code }...); } }; 

and in the corresponding compiler we will run // code with T , each of which is from left to right.

Please note that some compilers claiming to be C ++ 11 compilers may not compile the above.

The advantage of this method is that it hides the nasty "deploy and evaluate templates" code inside a function with a clear name. You write do_in_order once, and this is usually enough for almost any use of the array expansion trick.

There are two important reasons to use this type of esoteric syntax instead of the โ€œsimplerโ€ recursive solutions.

Firstly, it facilitates the work of the optimizer. Optimizers sometimes fail after a bunch of recursive calls.

Secondly, the sum of the names of the lengths of functions of function signatures for traditional recursive functions increases with O (n ^ 2). If you use helper types, the total length of the names is also O (n ^ 2). If you are not careful, this can lead to compilation time, connection time, and bloating of binary size.

In C ++ 1z, there are plans for some kind of "fold" syntax that can make esoteric parts higher than less esoteric ones.

+3
source share

I like overloaded functions and using a list of types:

 #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>(); } 
0
source share

All Articles