带有右值引用的C ++模板方法消歧规则

时间:2011-09-15 18:45:10

标签: c++ c++11

在以下示例中,调用#1,#1,#4和#4(按此顺序)。我希望可以调用#1,#1,#2,#3(折扣RVO)。

//-----------------------------------------------------------------------------
struct A
{
    A(){}                   //  1

    A(A&&){}                //  2

    A(const A&){}           //  3

    template<typename T>
    A(T&&){}                //  4

    template<>
    A(A&&){}                //  5

    template<typename T>
    A(const T&){}           //  6
};

//-----------------------------------------------------------------------------
A wtf(){ A x; return x; }

//-----------------------------------------------------------------------------
int main( int, char*[] )
{
    A a;
    A c = wtf();
    A b(c);
}

发生了什么以及为什么?!

注意:删除#5以使用GCC编译(它不是那么重要) - 上面的代码用VS2010编译。如果有人可以测试,我很想知道GCC的结果是否相同。

3 个答案:

答案 0 :(得分:1)

这是编译错误。

遗漏了关于return local表达式(以及throw local次尝试的隐式右值)的讨论,顺便说一下,这显然是一个编译器错误,因为 -

应该在示例代码中调用模板化构造函数的

none

没有模板函数可以被认为是复制构造函数,复制赋值运算符,移动构造函数或移动赋值运算符。

C ++ 11支持是不完整的,虽然gcc是the best它仍然是不完整和错误的。

答案 1 :(得分:0)

你有太多的构造函数用于真正的类。除了测试代码之外,这不会发生。

构造函数#4 TA&A& &&折叠为A&,与A b(c)的匹配优于#2 {{{ {1}}不是右值)或#3(c不是常量)。

答案 2 :(得分:-1)

return x;不会调用move构造函数,因为x可能不会过期。一般来说,它只是一个像其他任何一样的左值。 return std::move( x );通常是必要的。 (编辑:但请参阅下一段。)

有一条特殊规则要求编译器执行移动操作,而不是在未执行潜在省略的情况下执行复制省略,§12.8/ 32。我不确定你为什么没看到这个。也许编译器忽略了这条规则,因为他们不希望用户关闭elision。

您可能希望复制构造函数A( A const & )执行return副本,但模板A( T && )实现完美转发。 T &&被推断为A &,而const不是{{1}},因此更符合。

完全转发与复制构造函数结合时要非常小心。我有点惊讶语言没有特殊情况。据我所知,解决方案是在这种情况下使用SFINAE来禁用模板。