[2026] C++ RVO and NRVO | Return Value Optimization Complete Guide
이 글의 핵심
RVO vs NRVO: when the compiler elides copies on return, C++17 guaranteed elision for prvalues, NRVO heuristics, and interaction with move semantics.
What are RVO and NRVO?
RVO (Return Value Optimization) applies when returning temporaries. NRVO (Named Return Value Optimization) applies when returning a named local variable. Together they are the main examples of copy elision. 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
BigObject createObject() {
return BigObject(); // RVO — prvalue
}
BigObject createNamed() {
BigObject obj;
return obj; // NRVO — named object
}
RVO vs NRVO
| Optimization | Return form | C++17 mandatory | Notes |
|---|---|---|---|
| RVO | Temporary object | Yes (prvalue) | Very reliable |
| NRVO | Named variable | No | Same variable, same type paths |
Visual explanation
아래 코드는 mermaid를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
graph TD
A[Function Return] --> B{Return Form}
B -->|return Type...| C[RVO]
C --> D[C++17 guaranteed]
D --> E[0 copy/move]
B -->|return obj| F{Conditions}
F -->|Single var| G[NRVO attempt]
F -->|Multiple vars| H[Often move/copy]
G --> I{Compiler}
I -->|OK| E
I -->|No| J[Move ctor]
C++17 guaranteed elision (prvalues)
Returning prvalues must not introduce extra copies/moves in specified cases: 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// 실행 예제
Widget create() {
return Widget(); // ✅ Guaranteed: no copy, no move
}
Widget w = create(); // Direct construction in w
Even works with move-only types:
std::unique_ptr<int> create() {
return std::unique_ptr<int>(new int(42)); // ✅ Guaranteed
}
NRVO examples
Success case
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
std::vector<int> createVector(size_t n) {
std::vector<int> v(n, 0); // Single named variable
// ....fill v ...
return v; // ✅ NRVO likely
}
Failure case: Multiple returns
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
std::vector<int> conditional(bool flag) {
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = {4, 5, 6};
return flag ? v1 : v2; // ❌ NRVO blocked: two different objects
}
Real-world examples
1. Factory pattern with RVO
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Widget {
std::string name_;
std::vector<int> data_;
public:
Widget(std::string name, size_t size)
: name_(std::move(name)), data_(size) {}
static Widget createDefault() {
return Widget("default", 100); // RVO
}
static Widget createCustom(std::string name) {
Widget w(name, 1000);
// ....configure w ...
return w; // NRVO
}
};
// Usage
Widget w1 = Widget::createDefault(); // Zero copies
Widget w2 = Widget::createCustom("custom"); // Zero or one move
2. String builder
아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
std::string buildReport(const std::vector<int>& data) {
std::string report; // Single named variable
report.reserve(data.size() * 20);
for (int value : data) {
report += "Value: " + std::to_string(value) + "\n";
}
return report; // ✅ NRVO likely
}
3. Configuration loader
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
struct Config {
std::map<std::string, std::string> settings;
std::vector<std::string> plugins;
};
Config loadConfig(const std::string& path) {
Config cfg; // Single named variable
// ....parse file and fill cfg ...
cfg.settings[version] = "1.0";
cfg.plugins.push_back("logger");
return cfg; // ✅ NRVO likely
}
When optimization fails
1. Using std::move on return
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
Widget bad() {
Widget w;
return std::move(w); // ❌ Blocks NRVO, forces move
}
Widget good() {
Widget w;
return w; // ✅ NRVO or automatic move
}
Benchmark (GCC 13, -O2, 1M iterations):
| Version | Time (ms) | Operations |
|---|---|---|
return w; (NRVO) | 0 | Zero copies/moves |
return std::move(w); | 45 | 1M moves |
2. Multiple return variables
아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
Widget conditional(bool flag) {
Widget w1, w2;
return flag ? w1 : w2; // ❌ NRVO blocked
}
// ✅ Better: single variable
Widget conditional(bool flag) {
if (flag) {
return Widget(1); // RVO
}
return Widget(2); // RVO
}
3. Returning parameter
다음은 간단한 cpp 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
Widget process(Widget w) {
// ....modify w ...
return w; // ❌ No elision: w is parameter, not local
}
Checking if RVO/NRVO happened
Method 1: Constructor logging
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
struct Widget {
static int ctorCount, copyCount, moveCount;
Widget() { ++ctorCount; std::cout << "Ctor\n"; }
Widget(const Widget&) { ++copyCount; std::cout << "Copy\n"; }
Widget(Widget&&) noexcept { ++moveCount; std::cout << "Move\n"; }
~Widget() { std::cout << "Dtor\n"; }
};
int Widget::ctorCount = 0;
int Widget::copyCount = 0;
int Widget::moveCount = 0;
Widget create() {
Widget w;
return w;
}
int main() {
Widget::ctorCount = Widget::copyCount = Widget::moveCount = 0;
Widget w = create();
std::cout << "Ctor: " << Widget::ctorCount
<< ", Copy: " << Widget::copyCount
<< ", Move: " << Widget::moveCount << "\n";
// With NRVO: "Ctor: 1, Copy: 0, Move: 0"
}
Method 2: Compiler flags
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# Disable all elision (for testing)
g++ -std=c++17 -fno-elide-constructors test.cpp
# With elision (default)
g++ -std=c++17 test.cpp
Interaction with move semantics
Automatic move on return
C++11+ treats returned locals as rvalues if elision fails: 다음은 간단한 cpp 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
Widget create() {
Widget w;
return w; // Implicitly: return std::move(w) if NRVO fails
}
Priority:
- Try NRVO (zero operations)
- If NRVO fails, use implicit move
- If move unavailable, use copy
Compiler behavior
| Compiler | RVO | NRVO | Notes |
|---|---|---|---|
| GCC | Always (prvalue) | Usually | 5+ very good |
| Clang | Always (prvalue) | Usually | Excellent |
| MSVC | Always (prvalue) | Usually | 2017+ reliable |
| Test your compiler: | |||
| 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다. |
struct NonMovable {
NonMovable() = default;
NonMovable(const NonMovable&) = delete;
NonMovable(NonMovable&&) = delete;
};
NonMovable create() {
return NonMovable(); // ✅ OK with RVO
}
NonMovable createNamed() {
NonMovable obj;
return obj; // ⚠️ May fail without NRVO
}
Common mistakes
Mistake 1: Moving from const
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
const Widget create() {
Widget w;
return w; // ❌ Cannot move from const, must copy
}
// ✅ Remove const
Widget create() {
Widget w;
return w;
}
Mistake 2: Returning member
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
struct Container {
Widget widget_;
Widget getWidget() {
return widget_; // ❌ No elision: returning member
}
};
// ✅ Return by reference if ownership stays
const Widget& getWidget() const { return widget_; }
Mistake 3: Conditional with std::move
다음은 cpp를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
Widget create(bool flag) {
Widget w;
if (flag) {
// ....modify w ...
}
return std::move(w); // ❌ Blocks NRVO
}
// ✅ Trust the compiler
Widget create(bool flag) {
Widget w;
if (flag) {
// ....modify w ...
}
return w; // NRVO or automatic move
}
Advanced: Debugging elision
Assembly inspection
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# Generate assembly
g++ -std=c++17 -O2 -S test.cpp -o test.s
# Look for constructor calls
grep "call.*Widget" test.s
With RVO: Should see only one constructor call.
Without RVO: Multiple constructor/move calls.
Static analysis
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Use [[nodiscard]] to ensure return value is used
[[nodiscard]] Widget create() {
Widget w;
return w;
}
// Compiler warns if return value ignored
create(); // Warning: ignoring return value
Performance impact
Benchmark (GCC 13, -O2, 1M iterations):
| Type | RVO (prvalue) | NRVO (named) | Move | Copy |
|---|---|---|---|---|
std::string (SSO) | 0ms | 0ms | 15ms | 45ms |
std::vector<int> (100 elem) | 0ms | 0ms | 8ms | 120ms |
| Large struct (1KB) | 0ms | 0ms | 2ms | 180ms |
| Key insight: Elision is dramatically faster than even move operations. |
Related posts
Keywords
C++, RVO, NRVO, copy elision, C++17, optimization, return value optimization, move semantics