保证副本省略如何运作?

jot*_*tik 75 c++ copy-elision c++17

在2016年奥卢ISO C++标准会议上,标准委员会将一项名为" 保证副本省略"的提案通过简化的价值类别投票进入C++ 17.

保证副本省略如何运作?是否涵盖了某些已经允许复制省略的情况,或者是否需要更改代码来保证复制省略?

Nic*_*las 112

在许多情况下允许复制省略.但是,即使允许,代码仍然必须能够工作,好像副本没有被删除.也就是说,必须有一个可访问的副本和/或移动构造函数.

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

考虑这个功能:

T Func() {return T();}
Run Code Online (Sandbox Code Playgroud)

在非保证复制省略规则下,这将创建一个临时规则,然后从该临时规则移动到该函数的返回值.该移动操作可能会被省略,但T即使从未使用过,也必须具有可访问的移动构造函数.

同理:

T t = Func();
Run Code Online (Sandbox Code Playgroud)

这是复制初始化t.这将t使用返回值复制initialize Func.但是,T仍然必须有一个移动构造函数,即使它不会被调用.

保证复制省略重新定义了prvalue表达式的含义.前C++ 17,prvalues是临时对象.在C++ 17中,prvalue表达式只是可以实现临时表达的东西,但它不是暂时的.

如果使用prvalue初始化prvalue类型的对象,则不会实现临时.执行return T();此操作时,将通过prvalue初始化函数的返回值.由于该函数返回T,因此不会创建临时函数; 初始化prvalue只是直接初始化返回值.

要理解的是,由于返回值是prvalue,因此它还不是对象.它只是一个对象的初始化器,就像T()是.

当你这样做时T t = Func();,返回值的prvalue直接初始化对象t; 没有"创建临时和复制/移动"阶段.因为Func()'s返回值是一个等价的prvalue T(),t直接初始化T(),就像你已经完成的那样T t = T().

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

保证elision允许你做的一件事是返回不动的对象.例如,lock_guard无法复制或移动,因此您无法使用按值返回的函数.但是有了保证的复制权,你可以.

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

new T(FactoryFunction());
Run Code Online (Sandbox Code Playgroud)

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

因此,按值返回的工厂函数可以直接初始化堆分配的内存,甚至不知道它.当然,只要这些功能在内部遵循保证副本省略的规则.他们必须返回一个类型的prvalue T.

当然,这也有效:

new auto(FactoryFunction());
Run Code Online (Sandbox Code Playgroud)

如果你不喜欢写类型名.


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

T Func()
{
   T t = ...;
   ...
   return t;
}
Run Code Online (Sandbox Code Playgroud)

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

因此,命名返回值优化(NRVO)没有任何变化.

  • @ JohannesSchaub-litb:如果你完全了解C++标准的细节,那只是"模棱两可".对于99%的C++社区,我们知道"保证副本省略"指的是什么.提出该功能的实际论文甚至*标题为*"Guaranteed Copy Elision".添加"通过简化的价值类别"只会让用户感到困惑和难以理解.这也是一个误称,因为这些规则并没有真正"简化"围绕价值类别的规则.无论您是否喜欢,"保证副本省略"一词指的是此功能,而不是其他任何内容. (6认同)
  • @Icebone1000:不,对于这两个问题。一旦它有了名称,例如参数的名称,它就不再是纯右值了。并且保证省略*仅*适用于纯右值。 (2认同)