Can c++11 parameter packs be used outside templates?

c++, c++11, templates

Solution

void foo(int... args) {}

No you cannot write that.

But you can have the same effect with this approach:

template<typename ...Ints>
void foo(Ints... ints) 
{
   int args[] { ints... }; //unpack ints here
   //use args
}

With this approach, you can pass all `int` if you want. If any argument passed to `foo` is not `int` or convertible to `int`, the above code will result in compilation error, as it would be the case with `int ...args` approach if it were allowed.

You could also use `static_assert` to ensure all `Ints` are indeed `int` if you want that behaviour:

template<typename ...Ints>
void foo(Ints... ints) 
{
   static_assert(is_all_same<int, Ints...>::value, "Arguments must be int.");

   int args[] { ints... }; //unpack ints here
   //use args
}

Now you've to implement `is_all_same` meta-function which is not difficult to implement.

Alright, this is the basic idea. You can write more sophisticated code with variadic templates and with the help of some utility meta-functions and helper functions.

For lots of work that you can do with variadic arguments, you don't even need to store in `args[]` array, e.g if you want to print the arguments to `std::ostream`, then you could just do it as:

struct sink { template<typename ...T> sink(T && ... ) {} };

template<typename ...Ints>
void foo(Ints... ints) 
{
    //some code

     sink { (std::cout << ints)... };
}

Here you create a temporary object of type `sink` so that you use unpack the template arguments using list-initialization syntax.

Last you could just use `std::initializer_list<int>` itself:

void foo(initializer_list<int> const & ints) 
{

}

Or `std::vector<int>` in case if you need vector-like behavior inside `foo()`. If you use any of these, you have to use `{}` when calling the function as:

f({1,2,3});

That may not be ideal but I think with the advent of C++11 you will see such code very frequently!

Problem

I was wondering if I could have parameter packs consisting of a single, explicitly specified, type. For example, something like this: ``` #include <iostream> using namespace std; void show() { } template<typename First, typename... Rest> void show(First f, Rest... rest) { cout << f << endl; show(rest...); } void foo(int f, int... args) // error { show(f, args...); } int main() { foo(1, 2, 3); } ``` The problem I'm having is with the definition of `foo()`. With OS X clang++ version 5 (llvm 3.3svn) I get the error `error: type 'int' of function parameter pack does not contain any unexpanded parameter packs`. Of course, I can get it to compile by changing to `foo()` into a function template: ``` template<typename... Args> void foo(int f, Args... args) { show(f, args...); } ``` However now `foo()` will accept `int` for the first parameter, and anything output streamable for the rest. For example: ``` struct x { }; ostream& operator<<(ostream& o, x) { o << "x"; return o; } int main() { foo(1, 2, x(), 3); // compiles :( } ``` Now, I've seen the accepted solution here which suggests using type traits and `std::enable_if`, but that's cumbersome. They also suggested using `std::array` but I think a simple `std::initializer_list` works just fine and looks cleaner, like so: ``` void foo_impl(initializer_list<int> ints) { for(int i: ints) cout << i << endl; } template<typename... Args> void foo(int f, Args... args) { foo_impl({f, args...}); } struct x { }; ostream& operator<<(ostream& o, x) { o << "x"; return o; } int main() { foo(1, 2, 3); foo(1, 2, x(), 3); // no longer compiles // we also get an error saying no known conversion from 'x' to 'int' :) } ``` So that's neat. But the question remains, is this necessary? Is there really not a way to define a non-template function which accepts a parameter pack of specific type? Like this: ``` void foo(int... args) { } ```

Original source

Related problems