Perfect-forwaring of the variadic template parameters of a struct
c++, c++11, templates, variadic
Solution
In your first example :
template <typename... T>
struct S
{
void X(T&&... args)
{
Do(std::forward<T>(args)...);
}
};
`T` is evaluated to `V` when you declare `S<V> s`, therefore the only prototype of the generated `X` member function is :
void X(V&&);
Whereas in your second example :
template <typename... T>
struct S
{
template <typename... T2>
void X(T2&&... args)
{
Do(std::forward<T2>(args)...);
}
};
`T` is evaluated to `V&` when you call it with `s.X(v)` so the generated prototype of the member function `X` is :
void X(V& &&);
which becomes (with reference colapsing) :
void X(V&);
To answer your second question, you either need to repeat the variadic template parameters or use function overloading.
Problem
In my C++11 code, I have a variadic struct and a function that should use perfect-forwarding for the struct's variadic types such as this: ``` template <typename... T> struct S { void X(T&&... args) { Do(std::forward<T>(args)...); } }; ``` Just assume that `Do` is a free-standing variadic function. Given a type `struct V { int x, y; };` I want to call `S::X` like so: ``` S<V> s; V v = { 1, 2 }; s.X(V()); // Compiles s.X(v); // Does not compile ``` The last line produces the following error in Visual Studio 2013 and Visual Studio 2013 with the November 2013 CTP of the C++ compiler: ``` error C2664: 'void S<V>::X(V &&)' : cannot convert argument 1 from 'V' to 'V &&' You cannot bind an lvalue to an rvalue reference ``` I tried mingw 4.8.1 and got a similar error, so it doesn't seem to be a compiler or C++11-support problem: ``` Source.cpp:51:7: error: cannot bind 'V' lvalue to 'V&&' s.X(v); // error C2664: 'void S<V>::X(V &&)' : cannot convert argument 1 from 'V' to 'V &&' ^ Source.cpp:17:7: error: initializing argument 1 of 'void S<T>::X(T&& ...) [with T = {V}]' void X(T&&... args) ^ ``` I was surprised to find out that a call to `s.X(v)` wouldn't work, as that is what universal references and perfect-forwarding are all about, right? While trying to figure out what's going on, I first noticed that it does indeed work when X is a free-standing variadic function, and it also works if I change X to be 'doubly variadic', in a sense: ``` template <typename... T> struct S { template <typename... T2> void X(T2&&... args) { Do(std::forward<T2>(args)...); } }; ``` Now, calling both `s.X(v)` and `s.X(V())` work as expected, however, the relationship between the `T` and `T2` variadic template arguments is now unclear. Reading this stackoverflow question, I get the impression that the original version of X is in fact not using perfect-forwarding and universal references at all; instead, when `S`'s template arguments are expanded by the compiler, the definition of `S::X` is also expanded, hence the function's prototype is actually `void S::X(V&& v)`, in which case the error message makes perfect sense. Can anyone confirm this behavior? Is there a way to have a true perfect-forwarding/universal reference function within a variadic struct without repeating the variadic arguments? If my suspicion is indeed correct, is that a defect of the current C++ standard?