146 lines
7.1 KiB
Markdown
146 lines
7.1 KiB
Markdown
[class.copy.elision]
|
||
|
||
# 11 Classes [[class]](./#class)
|
||
|
||
## 11.9 Initialization [[class.init]](class.init#class.copy.elision)
|
||
|
||
### 11.9.6 Copy/move elision [class.copy.elision]
|
||
|
||
[1](#1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L6354)
|
||
|
||
When certain criteria are met, an implementation is
|
||
allowed to omit the creation of a class object from
|
||
a source object of the same type (ignoring cv-qualification),
|
||
even if the selected constructor and/or the
|
||
destructor for the object haveside effects[.](#1.sentence-1)
|
||
|
||
In such cases, the
|
||
implementation treats the source and target of the
|
||
omitted initialization as simply two different ways of
|
||
referring to the same object[.](#1.sentence-2)
|
||
|
||
If the first parameter of the
|
||
selected constructor is an rvalue reference to the object's type,
|
||
the destruction of that object occurs when the target would have been destroyed;
|
||
otherwise, the destruction occurs at the later of the times when the
|
||
two objects would have been destroyed without the
|
||
optimization[.](#1.sentence-3)
|
||
|
||
[*Note [1](#note-1)*:
|
||
|
||
Because only one object is destroyed instead of two,
|
||
and the creation of one object is omitted,
|
||
there is still one object destroyed for each one constructed[.](#1.sentence-4)
|
||
|
||
â *end note*]
|
||
|
||
This elision of object creation, called[*copy elision*](#def:copy_elision),
|
||
is permitted in the
|
||
following circumstances (which may be combined to
|
||
eliminate multiple copies):
|
||
|
||
- [(1.1)](#1.1)
|
||
|
||
in a return statement ([[stmt.return]](stmt.return "8.8.4 The return statement")) in
|
||
a function with a class return type,
|
||
when the [*expression*](expr.comma#nt:expression "7.6.20 Comma operator [expr.comma]") is the name of a non-volatile
|
||
object o with automatic storage duration (other than a function parameter or a variable
|
||
introduced by the [*exception-declaration*](except.pre#nt:exception-declaration "14.1 Preamble [except.pre]") of a[*handler*](except.pre#nt:handler "14.1 Preamble [except.pre]") ([[except.handle]](except.handle "14.4 Handling an exception"))),
|
||
the copy-initialization of the result object can be
|
||
omitted by constructing o directly
|
||
into the function call's result object;
|
||
|
||
- [(1.2)](#1.2)
|
||
|
||
in a [*throw-expression*](expr.throw#nt:throw-expression "7.6.18 Throwing an exception [expr.throw]") ([[expr.throw]](expr.throw "7.6.18 Throwing an exception")), when the operand
|
||
is the name of a non-volatile object o with automatic storage duration
|
||
(other than a function parameter or
|
||
a variable introduced by
|
||
the [*exception-declaration*](except.pre#nt:exception-declaration "14.1 Preamble [except.pre]") of a [*handler*](except.pre#nt:handler "14.1 Preamble [except.pre]"))
|
||
that belongs to a scope that does not contain
|
||
the innermost enclosing [*compound-statement*](stmt.block#nt:compound-statement "8.4 Compound statement or block [stmt.block]") associated with a [*try-block*](except.pre#nt:try-block "14.1 Preamble [except.pre]") (if there is one),
|
||
the copy-initialization of the exception object can be omitted by
|
||
constructing o directly into the exception object;
|
||
|
||
- [(1.3)](#1.3)
|
||
|
||
in a [coroutine](dcl.fct.def.coroutine "9.6.4 Coroutine definitions [dcl.fct.def.coroutine]"), a copy of a coroutine parameter
|
||
can be omitted and references to that copy replaced with references to the
|
||
corresponding parameter if the meaning of the program will be unchanged except for
|
||
the execution of a constructor and destructor for the parameter copy object;
|
||
|
||
- [(1.4)](#1.4)
|
||
|
||
when the [*exception-declaration*](except.pre#nt:exception-declaration "14.1 Preamble [except.pre]") of a[*handler*](except.pre#nt:handler "14.1 Preamble [except.pre]") ([[except.handle]](except.handle "14.4 Handling an exception")) declares an object o,
|
||
the copy-initialization of o can be omitted by treating
|
||
the [*exception-declaration*](except.pre#nt:exception-declaration "14.1 Preamble [except.pre]") as an alias for the exception
|
||
object if the meaning of the program will be unchanged except for the execution
|
||
of constructors and destructors for the object declared by the[*exception-declaration*](except.pre#nt:exception-declaration "14.1 Preamble [except.pre]")[.](#1.sentence-5)
|
||
[*Note [2](#note-2)*:
|
||
There cannot be a move from the exception object because it is
|
||
always an lvalue[.](#1.4.sentence-2)
|
||
â *end note*]
|
||
|
||
Copy elision is not permitted
|
||
where an expression is evaluated in a context
|
||
requiring a constant expression ([[expr.const]](expr.const "7.7 Constant expressions"))
|
||
and in constant initialization ([[basic.start.static]](basic.start.static "6.10.3.2 Static initialization"))[.](#1.sentence-6)
|
||
|
||
[*Note [3](#note-3)*:
|
||
|
||
It is possible that copy elision is performed
|
||
if the same expression
|
||
is evaluated in another context[.](#1.sentence-7)
|
||
|
||
â *end note*]
|
||
|
||
[2](#2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L6436)
|
||
|
||
[*Example [1](#example-1)*: class Thing {public: Thing(); ~Thing();
|
||
Thing(const Thing&);};
|
||
|
||
Thing f() { Thing t; return t;} Thing t2 = f();
|
||
|
||
struct A {void *p; constexpr A(): p(this) {}};
|
||
|
||
constexpr A g() { A loc; return loc;}constexpr A a; // well-formed, a.p points to aconstexpr A b = g(); // error: b.p would be dangling ([[expr.const]](expr.const "7.7 Constant expressions"))void h() { A c = g(); // well-formed, c.p can point to c or be dangling}
|
||
|
||
Here the criteria for elision can eliminate
|
||
the copying of the object t with automatic storage duration
|
||
into the result object for the function call f(),
|
||
which is the non-local object t2[.](#2.sentence-1)
|
||
|
||
Effectively, the construction of t can be viewed as directly initializing t2,
|
||
and that object's destruction will occur at program exit[.](#2.sentence-2)
|
||
|
||
Adding a move constructor to Thing has the same effect, but it is the
|
||
move construction from the object with automatic storage duration to t2 that is elided[.](#2.sentence-3)
|
||
|
||
â *end example*]
|
||
|
||
[3](#3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L6481)
|
||
|
||
[*Example [2](#example-2)*: class Thing {public: Thing(); ~Thing();
|
||
Thing(Thing&&);private: Thing(const Thing&);};
|
||
|
||
Thing f(bool b) { Thing t; if (b)throw t; // OK, Thing(Thing&&) used (or elided) to throw treturn t; // OK, Thing(Thing&&) used (or elided) to return t} Thing t2 = f(false); // OK, no extra copy/move performed, t2 constructed by call to fstruct Weird { Weird();
|
||
Weird(Weird&);};
|
||
|
||
Weird g(bool b) {static Weird w1;
|
||
Weird w2; if (b)return w1; // OK, uses Weird(Weird&)elsereturn w2; // error: w2 in this context is an xvalue}int& h(bool b, int i) {static int s; if (b)return s; // OKelsereturn i; // error: i is an xvalue}decltype(auto) h2(Thing t) {return t; // OK, t is an xvalue and h2's return type is Thing}decltype(auto) h3(Thing t) {return (t); // OK, (t) is an xvalue and h3's return type is Thing&&} â *end example*]
|
||
|
||
[4](#4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L6534)
|
||
|
||
[*Example [3](#example-3)*: template<class T> void g(const T&);
|
||
|
||
template<class T> void f() { T x; try { T y; try { g(x); }catch (...) {if (/*...*/)throw x; // does not movethrow y; // moves} g(y); } catch(...) { g(x);
|
||
g(y); // error: y is not in scope}} â *end example*]
|