Abseil Tip 107 참조 수명 연장

아래는 “이번 주의 팁 #107: 참조 수명 확장”에 대한 한글 번역입니다:


제목: “이번 주의 팁 #107: 참조 수명 확장”
원문 게시일: 2015년 12월 10일
작성자: Titus Winters (titus@google.com)


개요

TotW #101 이후로 참조와 수명에 대한 혼란이 보고되었습니다. 이 팁에서는 다음 질문에 대해 자세히 알아보겠습니다:
“참조 수명 확장은 언제 적용되나요?”

string Foo::GetName();

const string& name = obj.GetName();  // 이 코드는 안전할까요? 합법적일까요?

간단히 말해, 임시 객체(참조 대상)의 수명은 다음 조건에서만 연장됩니다:

  1. 지역 변수 const T& 또는 T&&임시 T를 반환하는 표현식의 결과로 초기화되거나,
  2. 임시 객체의 T 하위 객체(예: 구조체의 멤버 변수)를 참조할 때.

(Google 스타일에서는 주로 T&&는 무시합니다.)

표준 문서는 다소 복잡할 수 있으므로, 몇 가지 경계 사례를 통해 이를 명확히 설명하겠습니다.


수명 확장의 적용 사례와 예외

  1. T&에는 적용되지 않습니다. 반드시 const T&이어야 합니다.
    • T&로 초기화하려 하면 컴파일 오류가 발생합니다.
  2. 형 변환이 있는 경우에는 수명 확장이 작동하지 않습니다.
    • 예를 들어, stringconst absl::string_view&로 할당하면 string의 수명이 연장되지 않습니다.
    • 참고로 const absl::string_view&는 사용하지 않는 것이 좋습니다.
  3. 간접적으로 하위 객체를 얻는 경우 수명 확장이 작동하지 않습니다.
    • 컴파일러는 함수 호출(예: getter)을 통해 간접적으로 반환된 객체를 추적하지 않습니다.
    • 수명 확장은 표현식에서 반환된 임시 객체의 직접적인 멤버 변수에만 작동합니다.
  4. 형 변환이 허용되는 경우는 부모 클래스와 자식 클래스 간의 관계가 있는 경우뿐입니다.
    • 예를 들어, 부모 클래스 T와 자식 클래스 U가 있을 때, 자식 클래스 U의 임시 객체를 부모 클래스 T&로 참조할 수 있습니다. 하지만 이런 경우는 혼란을 초래할 수 있으므로 권장하지 않습니다.

수명 확장의 작동 방식

  • 수명이 연장된 임시 객체는 참조 변수가 스코프를 벗어날 때까지 유지됩니다.
  • 수명이 연장되지 않는 경우, 임시 객체는 문장의 끝(다음 세미콜론 ;)에서 파괴됩니다.

권장 사항

TotW #101에서 설명했듯이, 참조 초기화에서 수명 확장에 의존하는 것은 바람직하지 않습니다. 이는 유지보수성과 리뷰 과정에서 문제가 될 수 있습니다.

하지만, 수명 확장이 필요한 경우가 있습니다:

  • 임시 컨테이너에 대한 범위 기반 for 루프와 같은 사례입니다.
  • 그러나 이 경우에도, 확장은 임시 객체 전체에만 적용되며 하위 표현식에는 적용되지 않습니다.

다음은 올바르게 작동하는 예입니다:

std::vector<int> GetInts();
for (int i : GetInts()) { }  // 벡터의 수명 확장이 중요합니다.

// 문자열을 각 문자(char) 단위로 분리하여 string_view로 반환합니다.
std::vector<absl::string_view> Explode(const string& s);

// 벡터의 수명은 연장되지만, 임시 문자열의 수명은 연장되지 않습니다.
for (absl::string_view s : Explode(StrCat("oo", "ps"))) { }  // 잘못된 코드

다음은 수명 확장이 작동하지 않는 예입니다:

MyProto GetProto();

// MyProto가 스코프를 벗어나면서 sub_protos도 파괴됩니다. 
// 컴파일러는 sub_protos()가 하위 객체를 반환하는 것을 추적하지 못합니다.
for (const SubProto& p : GetProto().sub_protos()) { }  // 잘못된 코드

결론

  • 수명 확장은 임시 객체의 직접 반환된 결과에만 적용됩니다.
  • 형 변환이나 간접적인 하위 객체 참조에는 적용되지 않습니다.

가능하면 수명 확장에 의존하지 말고, 코드를 명시적으로 작성해 유지보수성과 가독성을 향상시키세요.