假设我有一个看起来像这样的函数:
SomeObject copy_maybe(bool make_new, const SomeObject& def)
{
if (make_new)
return SomeObject();
else
return def;
}
我称之为:
SomeObject obj;
obj = copy_maybe(true, obj);
如果没有复制省略,这显然会导致从obj
中创建的临时复制到copy_maybe
。但是,使用copy elision / RVO,副本是否可能从obj
发送到obj
?
更具体地说,在这些(或类似)条件下,复制操作符(void operator=(SomeObject const &other)
)是否有可能this
和&other
由于复制省略而相同?< / p>
我在Ideone上创建了一个test,它返回了不同的地址,但我只是想确保这个行为是由规范定义的。
答案 0 :(得分:4)
但是,对于copy elision / RVO,副本是否可能从
obj
发送到obj
?
没有。复制elison / RVO用于初始化变量的过程。由于您已使用obj
初始化SomeObject obj;
,因此您无法进行任何优化。将调用复制赋值运算符,并且将从函数中为调用站点的obj
分配obj
的值。
如果你有
SomeObject obj = copy_maybe(true, obj);
然后是的,复制elison可以(将在C ++ 17中)发挥作用。
请注意调用
SomeObject obj = copy_maybe(false, obj);
将obj
置于不确定状态,因为它与
SomeObject obj = obj;
答案 1 :(得分:2)
复制/移动作业永远不会被遗漏; elision仅在对象初始化时发生。
但是,有一种方法可以将elision应用于现有对象:
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。
答案 2 :(得分:1)
但是,对于copy elision / RVO,副本是否可能从
obj
发送到obj
?
不要“复制elision / RVO”
是的“将从obj
复制到obj
”,但仅作为复制作业。
建议在用户定义的复制分配功能中检查自我分配。当你这样做时,你的代码应该可以正常工作。
SomeObject& operator=(SomeObject const& rhs)
{
// Do nothing for self assignment.
if ( this != &rhs )
{
...
}
return *this;
}