我曾在几个地方看到过有关复制和移动构造函数的推荐签名,如下所示:
struct T
{
T();
T(const T& other);
T(T&& other);
};
复制构造函数需要一个const引用,而移动构造函数需要一个非const右值引用。
但就我所见,这会阻止我在从函数返回const对象时利用移动语义,例如下面的情况:
T generate_t()
{
const T t;
return t;
}
测试使用 VC11 Beta,调用的是 T
的拷贝构造函数而不是移动构造函数。即使使用 return std::move(t);
,仍然会调用拷贝构造函数。
我可以理解这种行为是有道理的,因为 t
是 const,所以不能绑定到 T&&
上。在移动构造函数签名中使用const T&&
是可行的,也是合理的,但问题在于,由于 other
是 const,如果需要将其成员设置为空,就无法实现 - 只有当所有成员都是标量或具有正确签名的移动构造函数时才能工作。
看起来,在一般情况下确保调用移动构造函数的唯一方法是首先使 t
不是 const,但我不喜欢这样做 - 将事物置为 const 是一个好习惯,我不希望 T
的客户端知道他们必须违反这个规范以提高性能。
因此,我的问题有两个方面;首先,移动构造函数应该使用 const 还是非 const 的右值引用?其次:我的推理是否正确? 我应该停止返回常量对象吗?
return T()
就可以了。虽然我仍然可以看到某些用例,但我认为它们很少见。当然,与左值引用相比,无法窃取资源的右值引用实际上没有任何价值。不过这是一个有趣的问题。 - Christian Rauconst
对象t
被构造到与非 const 返回值相同的位置。然后如果必要,非 const 返回值 可以 通过非 const 移动构造函数(或移动赋值运算符)由调用者移动。在构造的情况下,它再次符合复制省略,并且t
可以直接构造到使用generate_t()
调用进行初始化的任何内容中。是什么阻碍了 VC11 进行此优化? - Steve JessopT
的客户端不需要违反该形式以提高性能。然而,作为QoI问题,VC11(使用您提供的选项)已经失误了。总的来说,我认为说一个类的客户端不应该考虑哪些对象可以从中移动以提高性能并不完全正确。只是在这种情况下,我认为他们不需要这样做。 - Steve Jessopconst T&&
这将是不好的),那么移动构造函数将是可行的,但会被省略。因此,唯一的区别应该是移动还是复制是否被省略,但无论如何都应该被省略。 - Jonathan Wakely