문제

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?

도움이 되었습니까?

해결책

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.

다른 팁

You're correct: Universal references (and thus perfect forwarding) only work when the parameter is of type T&& for a deduced T. In your original case, the T comes from the struct's tempalte parameters, so it's not deduced.

I can't comment on wether it's a defect in the current standard, but consider this: perfect forwarding works by sometimes (in very specific cases) using U& instead of U for deducing T (where U is the actual argument type). That's not possible when the template parameter is already "fixed" by being the parameter of the class.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top