复制 elision/RVO 会导致从同一对象复制/移动吗?

Can copy elision/RVO cause a copy/move from the same object

本文关键字:复制 对象 移动 elision RVO      更新时间:2023-10-16

假设我有一个看起来像这样的函数:

SomeObject copy_maybe(bool make_new, const SomeObject& def)
{
if (make_new)
return SomeObject();
else
return def;
}

我这样称呼它:

SomeObject obj;
obj = copy_maybe(true, obj);

如果没有复制省略,这显然总是会导致从copy_maybe中创建的临时副本obj。但是,使用复制省略/RVO,复制是否有可能从obj发生到obj

更具体地说,在这些(或类似)条件下,在复制运算符(void operator=(SomeObject const &other))中,由于复制省略,this&other是否可能相同?

我在 Ideone 上创建了一个测试,它返回单独的地址,但我只想确保此行为由规范定义。

但是,使用复制 elision/RVO,是否有可能从obj复制到obj

不。 Copy elison/RVO 用于初始化变量的过程。 由于您已经使用SomeObject obj;初始化了obj,因此不会获得任何优化。 将调用复制赋值运算符,并将为调用站点中的obj分配函数中obj的值。

如果你有

SomeObject obj = copy_maybe(true, obj);

那么是的,复制 elison 可以(将在 C++17 中发挥作用。


请注意,呼叫

SomeObject obj = copy_maybe(false, obj);

将使obj处于不确定状态,因为它与

SomeObject obj = obj;

复制/移动赋值永远不能省略;省略只发生在对象的初始化时。

但是,有一种方法可以使省略应用于现有对象:

SomeObject obj;
new(&obj) auto(copy_maybe(false, obj);

C++17以一种有趣的方式定义了这一点。放置new发生在构造新对象之前。调用new放置称为"获取该对象的存储"。

该标准规定,当您为对象"获取存储"时,该存储中已有的任何对象的生存期都将终止(因为它们的存储正在被重用于新对象)。因此,最初声明的对象已结束其生存期。但它的析构函数不会被调用;如果您的程序依赖于在对象的生存期结束之前调用的析构函数,则您有 UB。

但这还是激怒了UB。为什么?因为obj的一生在copy_maybe被召唤之前就结束了。因此,copy_maybe将获得对不再存在的对象的引用。当copy_maybe访问对不存在对象的引用时,您将获得 UB。

同样地:

SomeObject obj = copy_maybe(false, obj);

这也激怒了UB。obj的初始化是非空的;因此,在初始化完成之前,其生存期不会开始。这发生在copy_maybe.但是copy_maybe被赋予了一个尚未开始其生命周期的对象引用。那是 UB。

但是,使用复制省略/RVO,复制是否有可能从obj发生到obj

否"复制省略号/RVO"。
是的,"复制将从obj发生到obj",但仅作为复制分配。

建议在用户定义的副本赋值函数中检查自赋值。当你这样做时,你的代码应该可以正常工作。

SomeObject& operator=(SomeObject const& rhs)
{
// Do nothing for self assignment.
if ( this != &rhs )
{
...
}
return *this;
}