From C++17 compilers are required to omit copy/move construction resulting in zero-copy pass-by-value semantics. Objects will be created directly in storage where they would copy/move.
- In return statement when operand is prvalue:
1 2 3 4 5 |
T f() { return T(); } f(); // only one call to default constructor of T |
- In the initialization of a variable, when initialization expression is a prvalue of the same type:
1 |
T x = T(T(f())); // only one call to default constructor of T, to initialize x |
C++17 core language specification of prvalues and temporaries is fundamentally different from that of the earlier C++ revisions: there is no longer a temporary to copy/move from. Another way to describe C++17 mechanics is “unmaterialized value passing”: prvalues are returned and used without ever materializing a temporary.
Temporary materialization
A prvalue of any complete type T can be converted to an xvalue of the same type T. This conversion initializes a temporary object of type T from the prvalue by evaluating the prvalue with the temporary object as its result object, and produces an xvalue denoting the temporary object. If T is a class or array of class type, it must have an accessible and non-deleted destructor.
1 2 3 4 5 6 |
struct S { int m; }; int i = S().m; // member access expects glvalue as of C++17; // S() prvalue is converted to xvalue |
Temporary materialization occurs in the following situations:
- when binding a reference to a prvalue;
- when performing a member access on a class prvalue;
- when performing an array-to-pointer conversion (see above) or subscripting on an array prvalue;
- when initializing an object of type std::initializer_list<T> from a braced-init-list; when typeid is applied to a prvalue (this is part of an unevaluated expression); when sizeof is applied to a prvalue (this is part of an unevaluated expression);
- when a prvalue appears as a discarded-value expression.
Before C++17 compiler was allowed to make optimization using RVO or NRVO, but it was not guaranteed. From C++17 copy elision is guaranteed.
Value expressions
– lvalue (so-called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [Example: If E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue. —end example]
– xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references. [Example: The result of calling a function whose return type is an rvalue reference is an xvalue. — end example]
– glvalue (“generalized” lvalue) is an lvalue or an xvalue.
– rvalue (so-called, historically, because rvalues could appear on the right-hand side of an assignment expression) is an xvalue, a temporary object or subobject thereof, or a value that is not associated with an object.
– prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [Example: The result of calling a function whose return type is not a reference is a prvalue. The value of a literal such as 12, 7.3e5, or true, is also a prvalue. —end example]
Compiler value optimizations
RVO (Return Value Optimization) – compiler can optimize code to reduce constructions of object. For example:
1 2 3 4 5 |
T f() { return T(); } T a = f(); // only one call to default constructor of T |
NRVO (Named Return Value Optimization) – optimization for code like:
1 2 3 4 |
T f() { T t("test", "test"); return t; } |
Related standard document: p0135r1.