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

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) { }


Solution 1:[1]

Why the foo_impl workaround, and not just use initialize_list<int> in foo's signature directly? It clarifies that you accept a variable-size argument list of said type.

Solution 2:[2]

As with Brian's answer, I realize this was originally intended for C++11, but in C++20 this can be solved in a very simple way using concepts:

#include <concepts>

void f(std::integral auto... ints)
{
    // ...
}

std::integral accepts any integral type, so it's a bit more general, if that is acceptable. If not, you can do something like the following:

#include <concepts>

template<class T>
concept exactly_int = std::same_as<int,T>;

void f(exactly_int auto... ints)
{
   // ...
}

To add a bit more explanation to this, the auto is essentially an implicit template, and the name before it is constraining what types are allowed. So in the first example, anything that satisfies std::integral (int,long,unsigned,char, etc.) will be allowed. The second allows only ints, since that is the only type that satisfies the concept that was defined.

There is an even simpler way to do this: Concepts when used as constrains use the type that is being constrained as its first argument, so you can simply write:

#include <concepts>

void f(std::same_as<int> auto... ints)
{
   // ...
}

Solution 3:[3]

You might specify the type you want to show:

#include <iostream>

template<typename T>
void show_type() {}

template<typename T, typename... Rest>
void show_type(const T& x, Rest... rest)
{
    std::cout << x << std::endl;
    show_type<T>(rest...);
}

template<typename... Args>
void foo(int x, Args... args)
{
    show_type<int>(x, args...);
}

struct X { };
std::ostream& operator<<(std::ostream& o, X)
{
    o << "x";
    return o;
}


int main()
{
    foo(1, 2, 3);
    foo(1, 2, 3.0); // Implicit conversion
    // or just
    show_type<int>(1, 2, 3);
    foo(1, 2, X()); // Fails to compile
}

Solution 4:[4]

I realize this is tagged C++11, but the features of C++17/1z works fantastically here so I figured its solution is worth posting:

template<typename... Ints>
void foo(Ints... xs)
{
    static_assert(std::conjunction<std::is_integral<Ints>...>::value);
    (std::cout << xs << '\n', ...);
}

Solution 5:[5]

You can't use a template like

<MyType i, MyType ... myTypes>

for function, but you cat use it for class/struct. Just change your function to a static struct member and it's done (sum example):

 template<int i, int ... ints>
 struct A <i, ints...> {
    static int Do() {
        return i + A<ints...>::Do();
    }
 };

This is not all job to do - a boundary struct is also needed. All the example (with Show() method):

#include <iostream>

using namespace std;

template<int ...> // declaration needed
struct A {};

template<int i>   // boundary
struct A <i>{
    static void Show() {
        cout << i << endl;
    }
};

template<int i, int ... ints> // main struct
struct A <i, ints...> {
    static void Show() {
        cout << i << endl;
        A<ints...>::Show();
    }
};

int main() {
    A<5, 45, 2, -100>::Show();
    cout << endl;
    A<-15, -3, 2>::Show();
    return 0;
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 xtofl
Solution 2
Solution 3
Solution 4
Solution 5 Sylwester