1. Problem It Solves
Returning a fresh object by value once appeared to depend on an optional optimization and an accessible move constructor. C++17 redefines important prvalue cases so the destination object is initialized directly.
This lesson reduces that broad problem to one fixed-input program so the language rule and its observable result can be checked independently.
2. Prerequisites
A C++17 compiler invoked with warnings enabled and the earlier lessons listed in the course order.
Know constructors, copy and move operations, return-by-value, temporaries, and value categories.
3. Core Idea
A prvalue initially represents initialization rather than a separate temporary object. In guaranteed cases such as returning Token{42} as Token, the result object is constructed directly; materialization occurs only when an actual object is required.
Keep the type, object lifetime, ownership, and evaluation boundary visible while reading the example; syntax is useful only when those semantics are understood.
4. Minimal Syntax
Token make_token() {
return Token{42};
}
Token token = make_token();5. How It Works
A factory returns a prvalue of exactly its declared class type.
C++17 initializes the function result and then the local destination without invoking deleted copy or move constructors.
The program prints
construct 42once, followed byvalue: 42, giving a small test oracle that can be compared with the prediction made before compilation.
6. Common Mistakes
Named return value optimization remains different: returning a named local may still require an accessible move or copy operation if optional NRVO is not performed.
A successful build is not proof of correct semantics. Recheck lifetimes, invalidation, ordering, error paths, and required headers or link flags for the real program.
7. When to Use It
Use this technique when factories naturally create and return a fresh value of the declared result type.
Choose a simpler C++11/14 form when the C++17 rule does not improve safety, clarity, or measured performance for the supported toolchains.
8. Simple Example
Both copy and move constructors are deleted, yet the program is well formed because no source object needs to be transferred in the guaranteed prvalue path.
The companion .cpp file has no input or external dependency. Predict the complete output, compile it, run it, then change one constant and explain the new result.
Complete sample code
Source file
cpp17/21_guaranteed_copy_elision_prvalue_materialization/main.cpp
#include <iostream>
class Token {
public:
explicit Token(int value) : value_(value) {
std::cout << "construct " << value_ << '\n';
}
Token(const Token&) = delete;
Token(Token&&) = delete;
int value() const { return value_; }
private:
int value_;
};
Token make_token() {
return Token{42};
}
int main() {
Token token = make_token();
std::cout << "value: " << token.value() << '\n';
}
9. Key Takeaways
Return values naturally; do not add
std::moveto a fresh prvalue, and distinguish guaranteed elision from optional NRVO.C++17 mode must be selected explicitly; a newer compiler default can otherwise hide a portability error.
Warnings, deterministic examples, and small assertions turn a remembered rule into evidence.
Document any lifetime, ownership, synchronization, or allocation contract at the API boundary.
10. Self-Check Questions
Easy — What problem does Guaranteed Copy Elision and prvalue Materialization address?
Medium — How many constructor messages appear, and why are deleted moves irrelevant?
Hard — Why does returning a named local have different requirements from returning
Token{42}?