C++ Temporary Objects: Lifetime, const&, RVO, and Pitfalls
이 글의 핵심
Guide to unnamed temporaries: when they die, lifetime extension, subobject exceptions, and avoiding dangling references.
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
// 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
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
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
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
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
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
// 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
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)
- Dangling reference
- Lifetime
- Copy elision
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?
Keywords
C++, temporary objects, lifetime, RVO, move semantics
Related posts
- C++ series
- Adapter pattern
- ADL
- Aggregate initialization