42

在几个地方,我看到推荐的复制和移动构造函数签名如下:

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);复制构造函数仍然被调用。

我可以看到这是有道理的,因为tconst 所以不应该绑定到T&&. 在移动构造函数签名中使用const T&&效果很好,并且很有意义,但是您会遇到问题,因为other是 const,如果需要将其成员清空,则不能将其清空 - 只有当所有成员都是标量时,它才会起作用或具有正确签名的移动构造函数。

它看起来是确保在一般情况下调用移动构造t函数以首先制作非常量的唯一方法,但我不喜欢这样做 - 构造事物是一种很好的形式,我不希望客户T知道他们必须反对这种形式才能提高性能。

所以,我想我的问题是双重的;首先,移动构造函数应该采用 const 还是非 const 右值引用?第二:我的推理是否正确?我应该停止返回 const 的东西吗?

4

4 回答 4

35

它应该是一个非const右值引用。

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

于 2012-05-26T22:21:40.810 回答
10

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

如果可以从 const 对象移动,通常意味着复制对象与从对象“移动”一样有效。在这一点上,拥有移动构造函数通常没有任何好处。

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

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

于 2012-05-26T22:38:27.647 回答
8

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

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

此外,它们被设计为在没有const 的情况下使用,我相信 const 右值引用的唯一用途是 Scott Meyers 在本次演讲中提到的非常神秘的东西(从 42:20 到 44:47)。

我的推理是否正确?我应该停止返回 const 的东西吗?

这是一个太笼统的问题,我认为无法回答。在这种情况下,我认为值得一提的是,有一些std::forward功能可以同时保留右值性和左值性以及常量性,并且如果您返回传递给它的任何内容,它还将避免像普通函数那样创建临时函数.

这种返回还会导致右值引用被“损坏”为左值引用,您通常不希望这样,因此,使用上述功能进行完美转发可以解决问题。

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

于 2012-05-26T22:38:39.273 回答
4

除了其他答案中所说的之外,有时移动构造函数或函数接受const T&&. 例如,如果您将一个const按值返回对象的函数的结果传递给构造函数,T(const T&)它将被调用而不是T(T&&)像人们可能期望的那样(参见g下面的函数)。

这就是删除接受的重载不是接受的重载的原因。constT&&std::refstd::crefT&&

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

struct s {};

void f (      s&);  // #1
void f (const s&);  // #2
void f (      s&&); // #3
void f (const s&&); // #4

const s g ();
s x;
const s cx;

f (s ()); // rvalue        #3, #4, #2
f (g ()); // const rvalue  #4, #2
f (x);    // lvalue        #1, #2
f (cx);   // const lvalue  #2

有关更多详细信息,请参阅本文

于 2018-05-31T04:49:25.140 回答