C++17coreadvanced
Guaranteed Copy Elision
C++17 · Practical Modern · Compiler flag: -std=c++17
Explanation
The compiler is now required (not merely permitted) to elide copies and moves when a prvalue is used to initialize an object of the same type. This means types that are neither copyable nor movable can be returned from functions by value.
Real-World Scenario
Return a non-movable RAII resource handle from a factory function without any performance penalty.
Quick Snippet
snippet.cpp
struct DatabaseConnection {
DatabaseConnection(const std::string& connStr) { /* open */ }
DatabaseConnection(const DatabaseConnection&) = delete;
DatabaseConnection(DatabaseConnection&&) = delete;
};
// Valid in C++17: guaranteed copy elision means no copy/move is needed
DatabaseConnection connect(const std::string& host) {
return DatabaseConnection(host);
}
auto conn = connect("db.example.com");Full Example
copy_elision.cpp
// CodeNebula - C++17 Example: Guaranteed Copy Elision
// Compile: g++ -std=c++17 -Wall -Wextra -o example copy_elision.cpp
// Demonstrates guaranteed copy elision (also called "mandatory RVO"). In C++17,
// returning a prvalue from a function directly initializes the target object.
// No copy/move constructor is even required to exist for prvalue returns.
#include <iostream>
#include <string>
// ------------------------------------------------------------------
// 1. Non-movable type: works in C++17 due to guaranteed elision
// ------------------------------------------------------------------
struct NonMovable {
int value;
explicit NonMovable(int v) : value(v) {
std::cout << " NonMovable(" << v << ") constructed" << std::endl;
}
// Deleted copy and move -- only legal in C++17 for prvalue returns
NonMovable(const NonMovable&) = delete;
NonMovable(NonMovable&&) = delete;
NonMovable& operator=(const NonMovable&) = delete;
NonMovable& operator=(NonMovable&&) = delete;
};
// Returns a prvalue -- no copy/move needed in C++17
NonMovable make_non_movable(int v) {
return NonMovable{v}; // guaranteed: directly constructs at call site
}
// ------------------------------------------------------------------
// 2. Tracking type: shows that no copy/move actually occurs
// ------------------------------------------------------------------
struct Tracker {
std::string name;
explicit Tracker(std::string n) : name(std::move(n)) {
std::cout << " Tracker(\"" << name << "\") constructed" << std::endl;
}
Tracker(const Tracker& o) : name(o.name + " [copy]") {
std::cout << " Tracker(\"" << name << "\") COPIED" << std::endl;
}
Tracker(Tracker&& o) noexcept : name(std::move(o.name) + " [move]") {
std::cout << " Tracker(\"" << name << "\") MOVED" << std::endl;
}
~Tracker() {
std::cout << " ~Tracker(\"" << name << "\")" << std::endl;
}
};
Tracker make_tracker(const std::string& n) {
return Tracker{n}; // prvalue return: guaranteed no copy/move
}
Tracker make_tracker_named() {
Tracker t{"named"}; // NRVO: named return value optimization (not guaranteed)
return t; // compiler may or may not elide
}
// ------------------------------------------------------------------
// 3. Chained prvalue: elision through multiple layers
// ------------------------------------------------------------------
Tracker make_outer() {
return make_tracker("chained"); // prvalue chain: still guaranteed
}
int main()
{
std::cout << "=== guaranteed elision: non-movable type ===" << std::endl;
NonMovable nm = make_non_movable(42);
std::cout << " nm.value = " << nm.value << std::endl;
// Without guaranteed elision, this would fail to compile (deleted move ctor)
std::cout << "\n=== guaranteed elision: prvalue return ===" << std::endl;
Tracker t1 = make_tracker("direct");
std::cout << " t1.name = " << t1.name << std::endl;
// Expected: only ONE construction, no copy/move
std::cout << "\n=== NRVO: named return (may or may not elide) ===" << std::endl;
Tracker t2 = make_tracker_named();
std::cout << " t2.name = " << t2.name << std::endl;
std::cout << "\n=== chained prvalue elision ===" << std::endl;
Tracker t3 = make_outer();
std::cout << " t3.name = " << t3.name << std::endl;
std::cout << "\n=== destructors ===" << std::endl;
return 0;
}
/*
Expected output (with guaranteed copy elision):
=== guaranteed elision: non-movable type ===
NonMovable(42) constructed
nm.value = 42
=== guaranteed elision: prvalue return ===
Tracker("direct") constructed
t1.name = direct
=== NRVO: named return (may or may not elide) ===
Tracker("named") constructed
t2.name = named
=== chained prvalue elision ===
Tracker("chained") constructed
t3.name = chained
=== destructors ===
~Tracker("chained")
~Tracker("named")
~Tracker("direct")
*/
Run command
g++ -std=c++17 -Wall -Wextra -o example copy_elision.cpp