在以下示例中,调用#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的结果是否相同。
答案 0 :(得分:1)
这是编译错误。
遗漏了关于return local
表达式(以及throw local
次尝试的隐式右值)的讨论,顺便说一下,这显然是一个编译器错误,因为 -
none 。
没有模板函数可以被认为是复制构造函数,复制赋值运算符,移动构造函数或移动赋值运算符。
C ++ 11支持是不完整的,虽然gcc是the best它仍然是不完整和错误的。
答案 1 :(得分:0)
你有太多的构造函数用于真正的类。除了测试代码之外,这不会发生。
构造函数#4 T
为A&
,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来禁用模板。