Andrei Alexandrescu gave an excellent talk entitled: Variadic Templates are Funadic.
He presents the following 3 expansions which are subltey different:
template <class... Ts> void fun( Ts... vs ) {
gun( A<Ts...>::hun(vs)...);
gun( A<Ts...>::hun(vs...));
gun( A<Ts>::hun(vs)...);
}
He explains:
Call 1:
Expands all Ts
for instatiation of class A
,
Then calls hun(vs)
Then expands all parameters again when passing them into gun
Call 2:
Expands all Ts
and all vs
separately
Call 3:
Expnads in lock step, ie:
Expand argument 1 of Ts
and Argument 1 of vs
Expand argument 2 of Ts
and Argument 2 of vs
Expand argument n of Ts
and Argument n of vs
Other discussion on variadic templates only seem to cover the simple variadic class templates and variadic functions such as typesafe printf etc. I am unsure as to how these different types of expansion effect the code and where each type would be useful.
Does anyone have some examples that demonstrate the application of each type of expansion?
Variadic functions are functions that can take a variable number of arguments. In C programming, a variadic function adds flexibility to the program. It takes one fixed argument and then any number of arguments can be passed.
Parameter packs (C++11) A parameter pack can be a type of parameter for templates. Unlike previous parameters, which can only bind to a single argument, a parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name.
Pack expansion A pattern followed by an ellipsis, in which the name of at least one parameter pack appears at least once, is expanded into zero or more comma-separated instantiations of the pattern, where the name of the parameter pack is replaced by each of the elements from the pack, in order. template<class...
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.
#include <iostream>
#include <memory>
#include <typeinfo>
#include <cstdlib>
#include <cxxabi.h>
template <typename T>
std::unique_ptr<char, void(*)(void*)>
type_name()
{
return std::unique_ptr<char, void(*)(void*)>
(
__cxxabiv1::__cxa_demangle(typeid(T).name(), nullptr,
nullptr, nullptr),
std::free
);
}
void display() {}
template <class T>
void
display()
{
std::cout << type_name<T>().get() << ' ';
}
template <class T, class T2, class ...Tail>
void
display()
{
std::cout << type_name<T>().get() << ' ';
display<T2, Tail...>();
}
template <class... Ts>
struct A
{
template <class... Us>
static
int
hun(Us... us)
{
std::cout << "A<";
display<Ts...>();
std::cout << ">::hun(";
display<Us...>();
std::cout << ")\n";
return 0;
}
};
template <class ...T>
void gun(T...) {}
template <class... Ts> void fun( Ts... vs )
{
std::cout << "gun( A<Ts...>::hun(vs)...);\n";
gun( A<Ts...>::hun(vs)...);
std::cout << "\ngun( A<Ts...>::hun(vs...));\n";
gun( A<Ts...>::hun(vs...));
std::cout << "\ngun( A<Ts>::hun(vs)...);\n";
gun( A<Ts>::hun(vs)...);
}
int main()
{
fun(1, 'a', 2.3);
}
Output:
gun( A<Ts...>::hun(vs)...);
A<int char double >::hun(int )
A<int char double >::hun(char )
A<int char double >::hun(double )
gun( A<Ts...>::hun(vs...));
A<int char double >::hun(int char double )
gun( A<Ts>::hun(vs)...);
A<int >::hun(int )
A<char >::hun(char )
A<double >::hun(double )
Cases 2 and 3 really are very common in any kind of code involving variadic packs.
template<typename... T>
void f(T&&... t)
{
// Case 2:
auto t2 = std::tuple<T...>(t...);
// Case 3:
auto t3 = std::make_tuple(std::forward<T>(t)...);
}
Looking at my own code, I can't find any surviving example of case 1. I may have used it in the past in some detail
namespace for a helper tempate, but I'm not sure. I don't think it's going to be common or even necessary most of the time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With