移动构造函数应该采用const还是非const rvalue引用?

时间:2012-05-26 22:11:55

标签: c++ c++11 rvalue-reference move-constructor

在一些地方,我看到了复制和移动构造函数的推荐签名:

struct T
{
    T();
    T(const T& other);
    T(T&& other);
};

复制构造函数采用const引用,移动构造函数采用非const rvalue引用。

据我所知,这可以防止我在从函数返回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,但我不喜欢这样做 - 构造事物很好表格,我不希望T的客户知道他们必须反对该表格以提高表现。

所以,我想我的问题是双重的;首先,移动构造函数应该采用const还是非const rvalue引用?第二:我在这种推理中是对的吗?我应该停止返回const的东西吗?

4 个答案:

答案 0 :(得分:28)

它应该是非const右值参考。

如果某个对象被放置在只读内存中,则无法从中窃取资源,即使其正式生命周期即将结束。允许在C ++中以const创建的对象存在于只读内存中(使用const_cast尝试更改它们会导致未定义的行为)。

答案 1 :(得分:10)

移动构造函数通常应采用非const引用。

如果可以从const对象移动,通常意味着复制对象和从其“移动”一样有效。此时,拥有移动构造函数通常没有任何好处。

你也是正确的,如果你有一个你可能想要移动的变量,那么它将需要是非const的。

据我所知,这就是Scott Meyers改变了他对C ++ 11函数中返回类类型对象的建议的原因。通过const限定值返回对象确实可以防止无意中修改临时对象,但它也会禁止从返回值移动。

答案 2 :(得分:8)

移动构造函数应该采用const还是非const rvalue引用?

应采用非常量右值参考。 rvalue引用首先在它们的const形式中没有意义只是因为你想要修改它们(在某种程度上,你想要“移动”它们,你想要自己的内部结构)。

此外,它们被设计为使用而不是 const而且我相信const rvalue引用的唯一用途是Scott Meyers在this中提到的非常神秘的话题(来自时间42:20到44:47)。

我在这种推理中是对的吗?我应该停止返回const的东西吗?

我认为这是一个太普遍的问题。在这种情况下,我认为值得一提的是,有std::forward功能可以保留rvalue-ness和lvalue-ness以及const-ness,它也可以避免创建一个临时函数,因为正常函数可以做到返回任何传递给它的东西。

此返回还会导致右值参考被“损坏”到左值参考,并且您通常不希望这样,因此,使用上述功能完美转发解决了这个问题。

话虽如此,我建议您只看一下我发布链接的演讲。

答案 3 :(得分:2)

除了在其他答案中所说的内容之外,有时候移动构造函数或函数有理由接受const T&&。例如,如果将按值返回const对象的函数的结果传递给构造函数,则会调用T(const T&)而不是T(T&&),因为人们可能会期望(参见function { {1}}以下)。

这是删除接受std::ref and std::cref的{strong> g const而非接受T&&的重载的重载的原因。

具体而言,重载解析期间的优先顺序如下:

T&&

有关详细信息,请参阅this article