임시 객체 (Temporary Object)

  • 정의: 이름 없는 객체. 보통 {} 초기화나 반환값으로 생성.
  • 수명: 선언된 라인에서 소멸 (단, 참조로 바인딩되면 수명 연장).
  Point {1, 2}; // unnamed temporary object
  
  • Value Category: prvalue (pure rvalue) → 좌변에 올 수 없고, 주소 연산자 (&) 사용 불가.
  • 수명 연장 방법:
    • const lvalue reference로 바인딩 시 연장됨.
    • rvalue reference로도 바인딩 가능.
  const Point& r2 = Point{1,1}; // 수명 연장
Point&& r3 = Point{1,1};      // 수명 연장
  
  • 활용: 함수 인자 전달 시 매우 유용.
  void draw_line(const Point& from, const Point& to);

draw_line(Point{0, 0}, Point{30, 30}); // 임시 객체 전달
  

복사 생략 (Copy Elision)

  • 정의: 복사 생성자 호출 없이 객체를 직접 생성하는 컴파일러 최적화.
  • 예시:
  Point pt = Point{1, 2}; // 복사 생략 (C++17부터는 보장)
  
  • C++17 이전: 최적화 여부는 컴파일러에 따라 다름. 복사 생성자가 삭제되거나 private이면 에러 발생 가능.
  • C++17 이후: Guaranteed Copy Elision → 문법적으로 복사 생략.

임시 객체 물질화 (Temporary Materialization)

  • 정의: 특정 상황에서만 임시 객체가 실제 메모리에 생성되는 과정.

개념

  • **임시 객체 물질화(Temporary Materialization)**는 prvalue(순수 rvalue) 표현식이 실제 메모리에 객체로 생성되는 과정을 의미합니다.
  • C++에서는 모든 prvalue가 항상 객체가 되는 게 아니라, 필요할 때만 실제 객체가 메모리에 생성됩니다.

prvalue는 언제 물질화되는가?

  1. 참조로 바인딩될 때:

    • prvalue를 const lvalue referencervalue reference로 바인딩할 경우 임시 객체가 물질화됩니다.
      const Point& r = Point{1, 2}; // 물질화 + 수명 연장
    Point&& r2 = Point{3, 4};     // 물질화 + 수명 연장
      
  2. Object로 초기화될 때:

    • prvalue를 object로 초기화할 때 물질화됩니다.
      Point p = Point{1, 2}; // 물질화
      
  3. 값을 무시하는 경우 (discarded-value expression):

    • 단순히 {}로 초기화만 하고 결과를 사용하지 않으면 물질화되지 않습니다.
      Point{1, 2}; // 물질화 X (discarded-value expression)
      

C++17의 변화

  • C++17 이전: {}로 생성한 prvalue는 대부분 물질화되어 임시 객체가 생성됩니다.
  • C++17 이후: prvalue는 표현식만 존재하고, 실제 메모리상의 객체가 필요할 때만 물질화됩니다. 메모리 효율이 향상됨.

예시로 살펴보기

  struct Point { int x, y; };

// 1. 물질화되지 않음 (discarded-value expression)
Point{1, 2};

// 2. 물질화됨 (object로 초기화)
Point p1 = Point{1, 2};

// 3. 물질화됨 (참조로 바인딩)
const Point& p2 = Point{1, 2};
  

참조 한정자 (Reference Qualifier)

  • 정의: 멤버 함수 뒤에 & 또는 &&을 붙여 lvalue/rvalue 객체에 따라 호출될 멤버 함수를 구분하는 문법.
  class X {
public:
    void f1() &  { std::cout << "lvalue\n"; }  // lvalue 객체에서 호출
    void f1() && { std::cout << "rvalue\n"; }  // rvalue 객체에서 호출
};
  
  • 예시:
  X obj;
obj.f1();        // lvalue → "lvalue"
X{}.f1();        // rvalue → "rvalue"
  
  • 의미:

    • void f1() &X& 타입의 객체를 위한 함수.
    • void f1() &&X&& 타입의 객체를 위한 함수.
  • 활용 예시: Getter와 Move:

  class StringWrapper {
    std::string data;
public:
    std::string& get() &            { return data; }             // lvalue
    const std::string& get() const & { return data; }             // const lvalue
    std::string&& get() &&          { return std::move(data); }  // rvalue
};
  

이렇게 하면 lvalue에서는 복사로, rvalue에서는 move로 가져올 수 있다.