본문으로 건너뛰기
Previous
Next
C++ Temporary Objects: Lifetime, const&, RVO, and Pitfalls

C++ Temporary Objects: Lifetime, const&, RVO, and Pitfalls

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

// 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;
}
  1. Default: temporaries die at the end of the full expression.
  2. Lifetime extension: binding a temporary to const T& or certain rvalue refs can extend lifetime to the reference’s scope.
  3. 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.

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


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

C++, Temporary Objects, Lifetime, RVO, Move Semantics 등으로 검색하시면 이 글이 도움이 됩니다.