read arguments from variadic template

You have to provide overrides for the functions for consuming the first N (usually one) arguments.

void foo() {
   // end condition argument pack is empty
}

template <class First, class... Rest> 
void foo(First first, Rest... rest) {
    // Do something with first
    cout << first << endl; 

    foo(rest...); // Unpack the arguments for further treatment
}

When you unpack the variadic parameter it finds the next overload.

Example:

foo(42, true, 'a', "hello");
// Calls foo with First = int, and Rest = { bool, char, char* }
// foo(42, Rest = {true, 'a', "hello"}); // not the real syntax

Then next level down we expand the previous Rest and get:

foo(true, Rest = { 'a', "hello"}); // First = bool

And so on until Rest contains no members in which case unpacking it calls foo() (the overload with no arguments).


Storing the pack if different types

If you want to store the entire argument pack you can use an std::tuple

template <class... Pack>
void store_pack(Pack... p) {
    std::tuple<Pack...> store( p... );
    // do something with store
}

However this seems less useful.

Storing the pack if it's homogeneous

If all the values in the pack are the same type you can store them all like this:

vector<int> reverse(int i) {
    vector<int> ret;
    ret.push_back(i);
    return ret;
}

template <class... R>
vector<int> reverse(int i, R... r) {
    vector<int> ret = reverse(r...);
    ret.push_back(i);
    return ret; 
}

int main() {
    auto v = reverse(1, 2, 3, 4);
    for_each(v.cbegin(), v.cend(), 
        [](int i ) { 
            std::cout << i << std::endl; 
        }
    );
}

However this seems even less useful.


If the arguments are all of the same type, you could store the arguments in an array like this (using the type of the first argument for the array):

template <class T, class ...Args>
void foo(const T& first, const Args&... args)
{
    T arr[sizeof...(args) + 1] = { first, args...};
}

int main()
{
    foo(1);
    foo(1, 10, 100, 1000);
}

If the types are different, I suppose you could use boost::any but then I don't see how you are going to find out outside of the given template, which item is of which type (how you are going to use the stored values).


Edit: If the arguments are all of the same type and you want to store them into a STL container, you could rather use the std::initializer_list<T>. For example, Motti's example of storing values in reverse:

#include <vector>
#include <iostream>
#include <iterator>

template <class Iter>
std::reverse_iterator<Iter> make_reverse_iterator(Iter it)
{
    return std::reverse_iterator<Iter>(it);
}

template <class T>
std::vector<T> reverse(std::initializer_list<T> const & init)
{

    return std::vector<T>(make_reverse_iterator(init.end()), make_reverse_iterator(init.begin()));
}

int main() {
    auto v = reverse({1, 2, 3, 4});
    for (auto it = v.begin(); it != v.end(); ++it) {
        std::cout << *it << std::endl;
    }
} 

Tags:

C++

C++11