[2026] C++ Temporary Objects: Lifetime, const&, RVO, and Pitfalls
이 글의 핵심
C++ temporaries: full-expression rules, lifetime extension with const& and rvalue refs, dangling pointers from c_str(), RVO/NRVO, and performance tips.
What are temporary objects?
Temporary objects are unnamed objects created while evaluating an expression. Usually they are destroyed at the end of the full expression, unless bound to const lvalue reference or certain rvalue references (lifetime extension rules apply).
std::string s = std::string("Hello"); // temporary then named
int x = 1 + 2; // 3 as a temporary value
Why care:
- Performance: extra temporaries cost time
- Safety: dangling references to dead temporaries
- Optimization: ties to RVO/NRVO and moves
Temporary vs named object:
| | Named | Temporary |
|---|--------|-----------|
| Name | Yes | No |
| Lifetime | Scope | Usually end of full expression |
| Extension | — | Possible with
const&| | Movable | Yes | Yes |
std::string name("Alice"); // lives until scope ends
std::string("Alice"); // destroyed at end of full expression
When temporaries appear
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Function return
std::string getName() {
return "Alice";
}
// Conversion
void func(std::string s) {}
func("Hello"); // const char* -> std::string temporary
// Operators
std::string s = "Hello" + std::string(" World");
// Explicit prvalue
Widget(10);
Lifetime rules
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Temp {
public:
Temp(int val) : value(val) {
std::cout << "ctor " << value << std::endl;
}
~Temp() {
std::cout << "dtor " << value << std::endl;
}
private:
int value;
};
void func() {
Temp(10); // destroyed before next statement
std::cout << "next line" << std::endl;
}
- Default: temporaries die at the end of the full expression.
- Lifetime extension: binding a temporary to
const T&or certain rvalue refs can extend lifetime to the reference’s scope. - Exception: a reference to a subobject of a temporary (e.g.
getOuter().inner) does not extend the whole temporary’s lifetime in the same way—classic dangling hazard.
Practical examples
Lifetime extension
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
std::string getName() {
return "Alice";
}
int main() {
// Dangerous
const char* ptr = getName().c_str();
// Safer
const std::string& name = getName();
const char* ptr2 = name.c_str();
}
Function arguments
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
void process(const Widget& w) {
std::cout << "process " << w.getValue() << std::endl;
}
int main() {
process(Widget(10)); // temporary lives for the call
}
Return and RVO
아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
std::vector<int> createVector(size_t size) {
std::vector<int> result(size);
for (size_t i = 0; i < size; i++) {
result[i] = static_cast<int>(i);
}
return result; // often no copy (RVO/NRVO)
}
Operator overloads returning temporaries
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
class Vector2 {
float x, y;
public:
Vector2(float x, float y) : x(x), y(y) {}
Vector2 operator+(const Vector2& other) const {
return Vector2(x + other.x, y + other.y);
}
};
Optimizing temporaries
다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// RVO
std::string func1() {
return std::string("Hello");
}
// NRVO
std::string func2() {
std::string result = "Hello";
return result;
}
// Move when RVO does not apply
std::string func3() {
std::string result = "Hello";
return result;
}
Common pitfalls
Dangling from c_str()
Bind the owning std::string first, then call c_str().
Non-const lvalue reference to temporary
Only const T& and T&& can bind to temporaries in the usual rules.
Subobject references
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
struct Inner { int value = 42; };
struct Outer { Inner inner; };
Outer getOuter() { return Outer{}; }
// Dangerous
const Inner& bad = getOuter().inner;
// Better
const Outer& outer = getOuter();
const Inner& ok = outer.inner;
Iterator into temporary container
Store the container, then take iterators from the stored object.
FAQ
Q1: When are temporaries created?
A: Returns, conversions, operators, explicit prvalues.
Q2: Lifetime?
A: Usually end of full expression; extended when bound per language rules.
Q3: Performance?
A: RVO/NRVO and moves remove most overhead—avoid redundant work in loops.
Q4: Avoid dangling?
A: Name the owner, use const std::string& extension carefully, avoid subobject refs to temporaries.
Q5: Optimization tips?
A: Prefer +=, reserve capacity, avoid redundant conversions, do not std::move returned locals blindly.
Q6: Can a non-const lvalue ref bind to a temporary?
A: No—use const& or rvalue references.
Related posts (internal links)
Practical tips
Debugging
- Warnings first
Performance
- Profile
Code review
- Conventions
Practical checklist
Before coding
- Right approach?
- Maintainable?
- Performance?
While coding
- Warnings?
- Edge cases?
- Errors?
At review
- Intent?
- Tests?
- Docs?