完美转换结构的可变参数模板参数

Axe*_*ier 5 c++ templates variadic c++11

在我的C++ 11代码中,我有一个可变结构和一个函数,它应该对struct的可变参数类型使用完美转发,例如:

template <typename... T>
struct S
{
    void X(T&&... args)
    {
        Do(std::forward<T>(args)...);
    }
};
Run Code Online (Sandbox Code Playgroud)

假设这Do是一个独立的可变参数函数.鉴于struct V { int x, y; };我想要这样调用的类型S::X:

S<V> s;
V v = { 1, 2 };

s.X(V());   // Compiles
s.X(v);     // Does not compile
Run Code Online (Sandbox Code Playgroud)

最后一行在Visual Studio 2013和Visual Studio 2013中使用C++编译器的2013年11月CTP产生以下错误:

error C2664: 'void S<V>::X(V &&)' : cannot convert argument 1 from 'V' to 'V &&'
You cannot bind an lvalue to an rvalue reference
Run Code Online (Sandbox Code Playgroud)

我尝试了mingw 4.8.1并得到了类似的错误,因此它似乎不是编译器或C++ 11支持问题:

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)
   ^
Run Code Online (Sandbox Code Playgroud)

我很惊讶地发现呼叫s.X(v)不起作用,因为这就是通用引用和完美转发的全部内容,对吧?在试图弄清楚发生了什么的时候,我首先注意到当X是一个独立的可变参数函数时它确实有效,并且如果我将X改为"双变量",它在某种意义上也有效:

template <typename... T>
struct S
{
    template <typename... T2>
    void X(T2&&... args)
    {
        Do(std::forward<T2>(args)...);
    }
};
Run Code Online (Sandbox Code Playgroud)

现在,调用都s.X(v)s.X(V())工作不如预期,但是,之间的关系TT2可变参数模板参数现在还不清楚.阅读这个stackoverflow问题,我得到的印象是X的原始版本实际上根本没有使用完美转发和通用引用; 相反,当S编译器扩展模板参数时,也会扩展定义S::X,因此实际上函数的原型void S::X(V&& v),在这种情况下,错误消息非常有意义.

谁能证实这种行为?有没有办法在可变结构中拥有一个真正的完美转发/通用引用函数而不重复可变参数?如果我的怀疑确实是正确的,那么这是当前C++标准的缺陷吗?

Dra*_*rax 4

在你的第一个例子中:

template <typename... T>
struct S
{
    void X(T&&... args)
    {
        Do(std::forward<T>(args)...);
    }
};
Run Code Online (Sandbox Code Playgroud)

TV在声明时求值S<V> s,因此生成的成员函数的唯一原型X是:

void X(V&&);
Run Code Online (Sandbox Code Playgroud)

而在你的第二个例子中:

template <typename... T>
struct S
{
    template <typename... T2>
    void X(T2&&... args)
    {
        Do(std::forward<T2>(args)...);
    }
};
Run Code Online (Sandbox Code Playgroud)

TV&当您调用它时,它会被求值s.X(v),因此生成的成员函数原型X是:

void X(V& &&);
Run Code Online (Sandbox Code Playgroud)

变成(参考崩溃):

void X(V&);
Run Code Online (Sandbox Code Playgroud)

要回答第二个问题,您需要重复可变参数模板参数或使用函数重载。