Question

I have seen a lot of links introducing the variadic templates. But I have never seen any compilable example that demonstrates this approach.

Could someone provide me with some links in which such compilable examples can be found?

Was it helpful?

Solution

Variadic templates are part of the C++0x standard which is not yet officially released. They are supported by gcc since version 4.3, but you need to enable support for C++0x by adding the compiler switch -std=c++0x.

OTHER TIPS

One of the simplest possible examples is the following implementation of max which isn't even templated on types.

int maximum(int n)
{
    return n;
}

template<typename... Args>
int maximum(int n, Args... args)
{
    return max(n, maximum(args...));
}

Only slightly more complex is the canonical printf implementation:

void printf(const char *s)
{
  while (*s)
  {
    if (*s == '%' && *(++s) != '%')
      throw "invalid format string: missing arguments";
    std::cout << *s++;
  }
}

template<typename T, typename... Args>
void printf(const char* s, T value, Args... args)
{
  while (*s)
  {
    if (*s == '%' && *(++s) != '%')
    {
      std::cout << value;
      printf(s, args...); // call even when *s == 0 to detect extra arguments
      return;
    }
    std::cout << *s++;
  }
  throw "extra arguments provided to printf";
}

Variadic templates are a C++0x feature that primarily targets authors of generic libraries. I would not expect to see them in "user code". For example, in the C++0x standard library they are used in a lot of places: std::function, std::async, std::reference_wrapper, std::tuple, std::packaged_task, ...

To give you an example I'll show you how a reference_wrapper might be implemented with respect to variadic templates:

template<class T>
class reference_wrapper
{
    T *ptr;
public:
    explicit reference_wrapper(T& thing) : ptr(&thing) {}
    explicit reference_wrapper(T&&     ) = delete;

    operator T&() const {return *ptr;}

    template<class... Args>
    decltype( declval<T&>()(declval<Args>()...) )
    operator()(Args&&... args) const
    {
        return (*ptr)(forward<Args>(args)...);
    }
};

This is not perfectly conforming to the standard draft but it is supposed to be compilable with little modification. It demonstrates multiple C++0x features:

  • deleted functions (disabling the constructor for rvalues)
  • rvalue references (detecting rvalue arguments to the constructor, perfect forwarding)
  • type deduction via decltype
  • standard library function template declval to create objects for the purpose of building an expression for decltype (GCC does not yet offer this function template. You have to write it yourself)
  • variadic templates (accepting an arbitrary number of parameters)

The purpose of the variadic member template is to forward arguments to the object referred to by ptr. This should work in case T is a function pointer type or a class type with overloaded function call operator.

cheers! s

A very simple example of variadic template:

Suppose we want to have a function which takes variable number of arguments and prints them all. For ex:

print("Hello", 1, 3.14, 5L);

For that functionality to work, we would basically require two functions:

First one, a function which takes variable number of arguments:

template<typename T, typename... Args>
void print(T t, Args ...args){
     std::cout << t << ", ";
     print(args...);
}

Some explanation:

1.) Parameter Packs denoted by ellipsis(...), that appear in parameter list.

typename...Args 
        |  | << Optional whitespace. Can have multiple whitespaces in between them
    Args...args

That means, these all are same.

typename ...args
typename...args
typename   ...   args

So, you don't have to worry about the correct position of the whitespace in there. Though, IMO at most one whitespace should be used as a best practice.

2.) Pack Expansion: A pattern followed by an ellipsis.

print(args...); //expand when you wish to use them

3.) Parameter pack accepts zero or more template args. So, print(T t, Args... args) accepts one or more args.


Once you understand that, we can visualize the call flow as below:

print("Hello", 1, 3.14, 5L);

translates into:

print(string, int, float, long);

which calls

print(int, float, long);

which calls

print(float, long);  // say Level 2

which calls

print(long);         // say Level 1

which calls

print();             // say Level 0

If you have followed the Point#3 carefully, you must have realized that print(T t, Args... args) can't handle call at Level 0.
So we need another function here with same name to catch up at any level >=0.


Second one, a function to grab the call at the top of call stack:

Catch at level 0:

void print(){}

or, Catch at level 1:

template<typename T>
void print(T t){ std::cout << t;}

or, Catch at level 2:

template<typename T, typename U>
void print(T t, U u){ std::cout << t << ", " << u;}

so on...

Any of these would work. Hope this helps you next time you go about writing such function or class.

This is an example of variadic templates that I put up on my blog: http://thenewcpp.wordpress.com/2011/11/23/variadic-templates-part-1-2/

It compiles. It demonstrates finding the largest type from a group of types.

#include <type_traits>

template <typename... Args>
struct find_biggest;

//the biggest of one thing is that one thing
template <typename First>
struct find_biggest<First>
{
  typedef First type;
};

//the biggest of everything in Args and First
template <typename First, typename... Args>
struct find_biggest<First, Args...>
{
  typedef typename find_biggest<Args...>::type next;
  typedef typename std::conditional
  <
    sizeof(First) >= sizeof(next),
    First,
    next
  >::type type;
};

Before C++11, you can create template only with the fixed count of parameters.

Firts template for the function with one parameter.

Second template for the function with two parameters. ... i.e.

Since C++11 you can write only one template, compiler will generate required function itself.

Good example http://eli.thegreenplace.net/2014/variadic-templates-in-c/

another syntax: expanding, e.g.

template<typename VAL, typename... KEYS>
class MyMaps
{
  typedef std::tuple< std::map<KEYS,VAL>... > Maps;
}

hence:

MyMaps<int,int,string>:Maps

is now actually:

std::tuple<std::map<int,int>,std::map<string,int> >
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top