保证复制清除如何工作?


Answers:


129

在许多情况下都允许进行复制省略。但是,即使允许,该代码仍然必须能够像不删除副本一样工作。即,必须有一个可访问的副本和/或移动构造函数。

保证复制省略重新定义了一些C ++的概念,例如,某些情况下在那里拷贝/移动可能被省略不竟招来复制/移动所有。编译器没有保留副本;该标准说,永远不会发生这种复制。

考虑以下功能:

T Func() {return T();}

在无保证的复制省略规则下,这将创建一个临时变量,然后从该临时变量移至函数的返回值。此举操作可以被省略,但T仍必须具有即使从未使用过它的访问移动构造函数。

类似地:

T t = Func();

这是的副本初始化t。这将复制初始化t,返回值为Func。但是,T即使它不会被调用,仍然必须具有move构造函数。

保证复制省略重新定义了一个prvalue表达式的含义。在C ++ 17之前的版本中,prvalue是临时对象。在C ++ 17中,prvalue表达式只是可以实现临时的东西,但还不是临时的。

如果使用prvalue初始化prvalue类型的对象,则不会实现任何临时对象。当您这样做时return T();,这将通过prvalue初始化函数的返回值。由于该函数返回T,因此不会创建任何临时函数;prvalue的初始化只是直接初始化返回值。

需要了解的是,由于返回值是prvalue,因此它还不是对象。就像T()是一样,它只是对象的初始化程序。

当您这样做时T t = Func();,返回值的prvalue直接初始化该对象t;没有“创建临时和复制/移动”阶段。由于Func()返回值是prvalue的等效值T()t因此直接由初始化T(),就像您已经做过一样T t = T()

如果以任何其他方式使用prvalue,则prvalue将实现一个临时对象,该临时对象将在该表达式中使用(如果没有表达式,则将其丢弃)。因此,如果您这样做了const T &rt = Func();,则prvalue将实现一个临时(T()用作初始化程序),其引用将rt与通常的临时生命周期扩展内容一起存储在中。

确保省略允许您做的一件事是返回固定的对象。例如,lock_guard不能被复制或移动,因此您不能具有按值返回它的函数。但只要保证复制省略,您就可以。

保证省略也可用于直接初始化:

new T(FactoryFunction());

如果按值FactoryFunction返回T,则此表达式不会将返回值复制到分配的内存中。相反,它将分配内存,并将分配的内存直接用作函数调用的返回值内存。

因此,按值返回的工厂函数可以直接初始化堆分配的内存,而无需了解它。当然,只要这些功能在内部遵循保证复制保留的规则即可。他们必须返回类型为的prvalue T

当然,这也可以:

new auto(FactoryFunction());

如果您不喜欢写类型名称。


重要的是要认识到上述保证仅适用于prvalue。也就是说,返回命名变量时无法保证:

T Func()
{
   T t = ...;
   ...
   return t;
}

在这种情况下,t必须仍然具有可访问的复制/移动构造函数。是的,编译器可以选择优化复制/移动。但是编译器仍必须验证是否存在可访问的复制/移动构造函数。

因此,命名返回值优化(NRVO)不会发生任何变化。


1
@BenVoigt:无论是否可以使用省略号,将不可复制的用户定义类型放入寄存器都不是ABI可以做的事情。
Nicol Bolas

1
既然规则是公开的,那么可能有必要使用“ prvalues are initializes”概念进行更新。
约翰内斯·绍布

6
@ JohannesSchaub-litb:如果您对C ++标准的细节了解得太多,那就只是“模棱两可”。对于99%的C ++社区,我们知道“保证复制省略”是指什么。提出该功能的实际论文甚至标题为“ Guaranteed Copy Elision”。添加“通过简化的值类别”只会使用户感到困惑并且难以理解。这也是用词不当,因为这些规则并没有真正“简化”围绕价值类别的规则。不管您是否喜欢,术语“保证复制省略”都指代此功能,仅此而已。
Nicol Bolas's

1
我因此希望能够获得一个prvalue并随身携带。我想这只是一个(一次)std::function<T()>
Yakk-亚当·内夫罗蒙特

1
@LukasSalich:这是一个C ++ 11问题。这个答案是关于C ++ 17功能的。
Nicol Bolas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.