保证复制省略如何工作? [英] How does guaranteed copy elision work?

查看:30
本文介绍了保证复制省略如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 2016 年奥卢 ISO C++ 标准会议上,一项名为 通过简化的值类别保证复制省略 被标准委员会投票通过 C++17.

At the 2016 Oulu ISO C++ Standards meeting, a proposal called Guaranteed copy elision through simplified value categories was voted into C++17 by the standards committee.

保证复制省略究竟是如何工作的?它是否涵盖了一些已经允许复制省略的情况,或者是否需要更改代码以保证复制省略?

How exactly does guaranteed copy elision work? Does it cover some cases where copy elision was already permitted, or are code changes needed to guarantee copy elision?

推荐答案

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

Copy elision was permitted to happen under a number of circumstances. However, even if it was permitted, the code still had to be able to work as if the copy were not elided. Namely, there had to be an accessible copy and/or move constructor.

保证复制省略重新定义了许多 C++ 概念,因此某些可以省略复制/移动的情况实际上根本不会引起复制/移动.编译器不会删除副本;标准规定这种复制永远不会发生.

Guaranteed copy elision redefines a number of C++ concepts, such that certain circumstances where copies/moves could be elided don't actually provoke a copy/move at all. The compiler isn't eliding a copy; the standard says that no such copying could ever happen.

考虑这个函数:

T Func() {return T();}

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

Under non-guaranteed copy elision rules, this will create a temporary, then move from that temporary into the function's return value. That move operation may be elided, but T must still have an accessible move constructor even if it is never used.

同样:

T t = Func();

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

This is copy initialization of t. This will copy initialize t with the return value of Func. However, T still has to have a move constructor, even though it will not be called.

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

Guaranteed copy elision redefines the meaning of a prvalue expression. Pre-C++17, prvalues are temporary objects. In C++17, a prvalue expression is merely something which can materialize a temporary, but it isn't a temporary yet.

如果您使用纯右值来初始化纯右值类型的对象,则不会具体化临时对象.当您执行 return T(); 时,这将通过纯右值初始化函数的返回值.由于该函数返回 T,因此没有创建临时文件;纯右值的初始化只是直接初始化返回值.

If you use a prvalue to initialize an object of the prvalue's type, then no temporary is materialized. When you do return T();, this initializes the return value of the function via a prvalue. Since that function returns T, no temporary is created; the initialization of the prvalue simply directly initilaizes the return value.

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

The thing to understand is that, since the return value is a prvalue, it is not an object yet. It is merely an initializer for an object, just like T() is.

当你做T t = Func();时,返回值的纯右值直接初始化对象t;没有创建临时和复制/移动"阶段.由于Func()的返回值是一个等价于T()的纯右值,t直接被T()<初始化/code>,就像你完成了T t = T().

When you do T t = Func();, the prvalue of the return value directly initializes the object t; there is no "create a temporary and copy/move" stage. Since Func()'s return value is a prvalue equivalent to T(), t is directly initialized by T(), exactly as if you had done T t = T().

如果以任何其他方式使用纯右值,则纯右值将具体化一个临时对象,该对象将在该表达式中使用(如果没有表达式,则将其丢弃).因此,如果您执行 const T &rt = Func();,纯右值将实现一个临时的(使用 T() 作为初始化程序),其引用将被存储在 rt 中,以及通常的临时生命周期扩展内容.

If a prvalue is used in any other way, the prvalue will materialize a temporary object, which will be used in that expression (or discarded if there is no expression). So if you did const T &rt = Func();, the prvalue would materialize a temporary (using T() as the initializer), whose reference would be stored in rt, along with the usual temporary lifetime extension stuff.

有保证的省略允许你做的一件事是返回固定的对象.例如,lock_guard 不能被复制或移动,所以你不能有一个按值返回它的函数.但是有了保证的复制省略,您就可以.

One thing guaranteed elision permits you to do is return objects which are immobile. For example, lock_guard cannot be copied or moved, so you couldn't have a function that returned it by value. But with guaranteed copy elision, you can.

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

Guaranteed elision also works with direct initialization:

new T(FactoryFunction());

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

If FactoryFunction returns T by value, this expression will not copy the return value into the allocated memory. It will instead allocate memory and use the allocated memory as the return value memory for the function call directly.

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

So factory functions that return by value can directly initialize heap allocated memory without even knowing about it. So long as these function internally follow the rules of guaranteed copy elision, of course. They have to return a prvalue of type T.

当然,这也有效:

new auto(FactoryFunction());

如果你不喜欢写类型名.

In case you don't like writing typenames.

重要的是要认识到上述保证仅适用于纯右值.也就是说,当你返回一个 named 变量时,你得不到任何保证:

It is important to recognize that the above guarantees only work for prvalues. That is, you get no guarantee when returning a named variable:

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

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

In this instance, t must still have an accessible copy/move constructor. Yes, the compiler can choose to optimize away the copy/move. But the compiler must still verify the existence of an accessible copy/move constructor.

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

So nothing changes for named return value optimization (NRVO).

这篇关于保证复制省略如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆