Copy elision
复制省略
从C++11%29构造函数开始,优化复制和移动%28,从而产生零拷贝传递值语义。
解释
Under the following circumstances, the compilers are required to omit the copy- and move- constructors of class objects even if copy/move constructor and the destructor have observable side-effects: In initialization, if the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object: T x = T(T(T()) // only one call to default constructor of T, to initialize x In a function call, if the operand of a return statement is a prvalue and the return type of the function is the same as the type of that prvalue. T f() { return T{}; } T x = f( // only one call to default constructor of T, to initialize x T* p = new T(f() // only one call to default constructor of T, to initialize *p | (since C++17) |
---|
- 在初始化过程中,如果初始化器表达式是prvalue,并且源类型的cv非限定版本与目标类相同,则初始化器表达式用于初始化目标对象:tx=T%28t%28t%28%29%29%29;//只调用T的默认构造函数一次以初始化x
- 在函数调用中,如果返回语句是prvalue函数的返回类型与该prvalue的类型相同。
Tf%28%29{返回T{};}Tx=f%28%29;//只调用一个默认构造函数T,以初始化xT%2AP=新的T%28F%28%29%29;//只调用T的默认构造函数来初始化%2AP
%28自C++17%29
在以下情况下,编译器可以省略类对象的C++11%29构造函数的复制和移动-%28,即使复制/移动%28,因为C++11%29构造函数和析构函数有明显的副作用。
- 如果函数按值返回类类型,则返回语句%27s表达式是具有自动存储持续时间的非易失性对象的名称,即函数参数%27t或CATCH子句参数,并且具有相同的类型%28忽略顶层。简历-资格%29作为函数的返回类型,然后复制/移动%28,因为省略了C++11%29。当构造该本地对象时,它直接在存储中构造,否则函数%27s的返回值将被移动或复制到该存储中。这种复制省略的变体被称为NRVO,“命名返回值优化”。当未绑定到任何引用的无名临时对象被移动或%28由于C++11%29复制到相同类型的对象%28忽略顶级cv限定%29时,由于C++11%29被省略了复制/移动%28。当构造临时的时候,它直接构建在存储中,否则它会被移动,或者%28,因为C++11%29被复制到。当未知的临时参数是返回语句的参数时,复制省略的这种变体称为RVO,即“返回值优化”。%28直到C++17%29此优化是强制性的,请参见上文。%28自C++17%29
- 当未绑定到任何引用的无名临时对象被移动或%28自C++11%29复制到相同类型的对象%28忽略顶层时简历-资格%29,由于C++11%29省略了复制/移动%28。当构造临时的时候,它直接构建在存储中,否则它会被移动,或者%28,因为C++11%29被复制到。当未知的临时参数是返回语句的参数时,复制省略的这种变体称为RVO,即“返回值优化”。
%28,直到C++17%29,此优化是强制性的;参见上文。
%28自C++17%29
In a throw-expression, if the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and whose scope does not extend past the innermost try-block (if there is a try-block), then copy/move is omitted. When that local object is constructed, it is constructed directly in the storage where the exception object would otherwise be moved or copied to. When handling an exception, if the argument of the catch clause is of the same type (ignoring top-level cv-qualification) as the exception object thrown, the copy is omitted and the body of the catch clause accesses the exception object directly, as if caught by reference. This is disabled if such copy elision would change the observable behavior of the program for any reason other than skipping the copy constructor and the destructor of the catch clause's argument (for example, if the catch clause argument is modified, and the exception object is rethrown with throw;). | (since C++11) |
---|
- 在...抛出,如果操作数是具有自动存储持续时间的非易失性对象的名称,即%27T为函数参数或CATCH子句参数,且如果存在TRY块%29,则其范围不会扩展到最内部的TRY块%28,则省略复制/移动。当构造该本地对象时,它直接在存储中构造,否则异常对象将被移动或复制到该存储中。
- 在处理异常时,如果捕获条款与抛出的异常对象相同类型%28忽略顶级cv限定%29,则省略副本,CATCH子句的主体直接访问异常对象,就像通过引用捕获一样。如果这样的复制省略除了跳过CATCH子句%27s参数%28的复制构造函数和析构函数之外,还会更改程序的可观察行为,例如,如果修改了CATCH子句参数,并且异常对象被重新抛出,则禁用此选项。
throw;
29%。
%28自C++11%29
当发生复制省略时,实现将省略的复制/移动%28的源和目标作为引用同一对象的两种不同方式来处理,因为C++11%29操作就是两种不同的方法。而该对象的销毁发生在两个对象在没有优化%28的情况下被销毁的时候,除非所选构造函数的参数是对对象类型的rvalue引用。当目标自C++17%29以来被销毁%29%28时,就会发生销毁。
可以链接多个副本以消除多个副本。
In constant expression and constant initialization, all copy elision is guaranteed (note: this is by post-C++14 defect report CWG 2022): struct A { void *p; constexpr A(): p(this) {} }; constexpr A g() { A a; return a; } constexpr A a; // a.p points to a constexpr A b = g( // b.p points to b (NRVO guaranteed) void g() { A c = g( // c.p may point to c or to an ephemeral temporary } | (since C++14) |
---|
- 在常数表达式和常数初始化,所有副本省略保证%28注:这是由后C++14缺陷报告cwg 2022%29:结构A{空%2AA%28%29:P%28这%29{};A g%28%29{a;返回a;}Ab=g%28%29;//b.p指向b%28 NRVO保证%29 g%28%29{Ac=g%28%29;//C.P可指向c或临时的临时} %28自C++14%29注记复制省略是唯一允许的优化形式%28,直到C++14%29,这是两种允许的优化形式之一,同时分配省略和扩展,%28自C++14%29以来,可以改变明显的副作用。因为某些编译器并不是在允许使用%28e的所有情况下执行复制省略。g.在调试模式%29中,依赖复制/移动构造函数和析构函数的副作用的程序是不可移植的。当复制-省略发生在C++17%29之前,在没有保证复制的情况下,如果发生了%28,因为C++17%29并且没有调用复制/移动构造函数,如果在所有的%29上没有进行优化,那么它必须仍然存在并访问%28 As,否则程序就会形成错误。在返回语句或抛出表达式中,如果编译器不能执行复制省略,但复制省略的条件满足或将满足,则除外,源是函数参数。即使由lvalue指定对象,编译器也将尝试使用移动构造函数;有关详细信息,请参阅返回语句。%28自C++11%29例二次#include <iostream> #include <vector> struct Noisy { Noisy() { std::cout << "constructed\n"; } Noisy(const Noisy&) { std::cout << "copy-constructed\n"; } Noisy(Noisy&&) { std::cout << "move-constructed\n"; } ~Noisy() { std::cout << "destructed\n"; } }; std::vector<Noisy> f() { std::vector<Noisy> v = std::vector<Noisy>(3 // copy elision when initializing // v from a temporary // (guaranteed in C++17) return v; // NRVO from v to the returned nameless temporary (not guaranteed in C++17) } // or the move constructor is called if optimizations are disabled void g(std::vector<Noisy> arg) { std::cout << "arg.size() = " << arg.size() << '\n'; } int main() { std::vector<Noisy> v = f( // copy elision in initialization of v // from the result of f() (guaranteed in C++17) g(f() // copy elision in initialization of the // parameter of g() from the result of f() // (guaranteed in C++17) }二次可能的产出:二次constructed constructed constructed constructed constructed constructed arg.size() = 3 destructed destructed destructed destructed destructed destructed二次缺陷报告以下行为更改缺陷报告追溯应用于先前发布的C++标准。博士适用于公布的行为正确行为CWG 2022C++14在常量表达式中,复制省略是可选的。复制强制另见
- 复制初始化
- 复制构造函数
- 移动构造函数
© cppreference.com
在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。