const 참조 클래스 멤버는 임시 수명을 연장합니까?


171

왜 이런가요?

#include <string>
#include <iostream>
using namespace std;

class Sandbox
{
public:
    Sandbox(const string& n) : member(n) {}
    const string& member;
};

int main()
{
    Sandbox sandbox(string("four"));
    cout << "The answer is: " << sandbox.member << endl;
    return 0;
}

다음을 출력하십시오.

정답은:

대신에:

답은 : 4


39
그리고 더 재미있게 쓰기 위해 글을 쓴 cout << "The answer is: " << Sandbox(string("four")).member << endl;다면 효과가있을 것입니다.

7
@RogerPate 이유를 설명해 주시겠습니까?
Paolo M

16
호기심이 많은 사람의 경우, Roger Pate 예제는 string ( "four") 이 일시적이고 전체 expresion이 끝날 때 임시가 소멸 되기 때문에 작동 한다고 게시했습니다 . 따라서 그의 예제에서 SandBox::member읽을 때 임시 문자열 은 여전히 ​​살아 있습니다.
PcAF

1
질문 : 같은 클래스를 작성하는 것은 위험하기 때문에, 같은 클래스에 임시 변수 전달에 대한 컴파일러 경고가 , 또는 디자인 가이드 라인 작성 클래스 저장 참조 것을 금지하는가 (Stroustroup에?)? 참조 대신 포인터를 저장하는 설계 지침이 더 좋습니다.
그림 판당고

@PcAF : 생성자가 종료 된 string("four")후가 아니라 전체 표현식의 끝에서 임시 가 파괴되는 이유를 설명해 주 Sandbox시겠습니까? Potatoswatter의 답변에 따르면 생성자의 ctor-initializer (§12.6.2 [class.base.init])에서 참조 멤버에 대한 임시 바인딩은 생성자가 종료 될 때까지 지속됩니다.
Taylor Nichols

답변:


166

지역 const 참조 만으로 수명이 연장됩니다.

표준은 이러한 행동을 §8.5.3 / 5, [dcl.init.ref], 참조 선언의 초기화에 관한 섹션에서 지정합니다. 예제의 참조는 생성자의 argument nn바인딩되어 있으며 개체 가 범위를 벗어나면 바인딩되지 않습니다 .

수명 연장은 함수 인수를 통해 전이되지 않습니다. §12.2 / 5 [class.temporary] :

두 번째 컨텍스트는 참조가 임시에 바인딩 될 때입니다. 참조가 바인드 된 임시 또는 임시가 바인드 된 서브 오브젝트에 대한 완전한 오브젝트 인 임시는 아래에 지정된 것을 제외하고 참조의 수명 동안 지속됩니다. 생성자의 ctor-initializer (§12.6.2 [class.base.init])에서 참조 멤버에 대한 임시 바인딩은 생성자가 종료 될 때까지 지속됩니다. 함수 호출 (§5.2.2 [expr.call])에서 참조 매개 변수에 대한 임시 바인딩은 호출을 포함하는 전체 표현식이 완료 될 때까지 지속됩니다.


49
보다 인간 친화적 인 설명을 보려면 GotW # 88도 참조하십시오. herbsutter.com/2008/01/01/…
Nathan Ernst

1
표준이 "두 번째 컨텍스트가 참조가 prvalue에 바인드 될 때"라고 말하면 더 명확하다고 생각합니다. 영업 이익의 코드에서 당신은이 말할 수 member초기화하기 때문에, 임시에 바인딩 membern바인딩 수단 member같은 객체를가 n결합되고, 그 사실이 경우 임시 객체입니다.
MM

2
@MM prvalue를 포함하는 lvalue 또는 xvalue 이니셜 라이저가 prvalue를 확장하는 경우가 있습니다. 제 제안서 P0066 은 업무 상태를 검토합니다.
Potatoswatter

1
C ++ 11부터 Rvalue 참조는 한정자를 요구하지 않고 임시 수명을 연장합니다 const.
GetFree

3
예 @KeNVinFavo, 죽은 개체를 사용하여 UB 항상
Potatoswatter

30

발생한 일을 설명하는 가장 간단한 방법은 다음과 같습니다.

main ()에서 문자열을 만들어 생성자에 전달했습니다. 이 문자열 인스턴스는 생성자 내에서만 존재했습니다. 생성자 내에서이 인스턴스를 직접 가리 키도록 멤버를 할당했습니다. scope가 생성자를 떠날 때 문자열 인스턴스가 삭제되고 멤버가 더 이상 존재하지 않는 문자열 객체를 가리 켰습니다. Sandbox.member가 범위 외부의 참조를 가리 키도록하면 해당 외부 인스턴스가 범위에 포함되지 않습니다.

원하는 동작을 표시하도록 프로그램을 수정하려면 다음과 같이 변경하십시오.

int main()
{
    string temp = string("four");    
    Sandbox sandbox(temp);
    cout << sandbox.member << endl;
    return 0;
}

이제 temp는 생성자 끝이 아닌 main () 끝에서 범위를 벗어납니다. 그러나 이것은 나쁜 습관입니다. 멤버 변수는 인스턴스 외부에 존재하는 변수에 대한 참조가되어서는 안됩니다. 실제로는 해당 변수가 범위를 벗어날 시점을 절대 알 수 없습니다.

내가 권장하는 것은 Sandbox.member를 a로 정의하는 const string member;것입니다 . 이것은 멤버 변수를 임시 매개 변수로 할당하는 대신 임시 매개 변수의 데이터를 멤버 변수에 복사합니다.


내가 이렇게하면 : const string & temp = string("four"); Sandbox sandbox(temp); cout << sandbox.member << endl;여전히 작동합니까?
이브

@Thomas이 const string &temp = string("four");같은 결과를 제공 const string temp("four"); 당신이 사용하지 않는 한, decltype(temp)특별히
MM

@ MM 감사합니다.이 질문을 완전히 이해했습니다.
이브

However, this is bad practice.- 왜? 임시 개체와 포함 개체가 모두 같은 범위에서 자동 저장소를 사용하는 경우 100 % 안전하지 않습니까? 그렇게하지 않으면 문자열이 너무 커서 복사하기에 너무 비싸다면 어떻게 하시겠습니까?
최대

2
클래스가 올바른 범위를 갖도록 임시로 전달 된 클래스를 강제 실행하지 않기 때문에 @max. 그것은 언젠가이 요구 사항을 잊어 버렸고 유효하지 않은 임시 값을 전달하면 컴파일러가 경고하지 않음을 의미합니다.
Alex Che

5

기술적으로 말하면,이 프로그램은 실제로 표준 출력 (처음으로 버퍼링 된 스트림)으로 아무것도 출력하지 않아도됩니다.

  • cout << "The answer is: "비트가 발광 할 "The answer is: "버퍼 표준 출력의.

  • 그런 다음 << sandbox.member비트는에 매달려있는 참조를 제공하여 정의되지 않은 동작operator << (ostream &, const std::string &) 을 호출합니다 .

이 때문에 아무 일도 일어나지 않습니다. 프로그램이 정상적으로 작동하거나 stdout을 비우지 않고도 작동이 중단 될 수 있습니다. "응답은 다음과 같습니다."라는 텍스트가 화면에 나타나지 않습니다.


2
UB가 있으면 전체 프로그램의 동작이 정의되지 않습니다. 실행의 특정 지점에서만 시작되지는 않습니다. 그래서 우리는 "The answer is: "어디에서나 쓰여질 것이라고 말할 수 없습니다 .
Toby Speight

0

샌드 박스 생성자가 반환되면 임시 문자열이 범위를 벗어 났고이 문자열이 차지한 스택이 다른 목적으로 회수되었습니다.

일반적으로 참조를 장기적으로 유지해서는 안됩니다. 참조는 인수 또는 로컬 변수에 적합하며 클래스 멤버는 아닙니다.


7
"절대"는 엄청나게 강한 단어입니다.
Fred Larson

17
객체에 대한 참조를 유지할 필요가없는 한 절대 클래스 멤버가 되지 마십시오 . 사본이 아닌 다른 객체에 대한 참조를 유지해야하는 경우가 있습니다. 이러한 경우 참조는 포인터보다 명확한 솔루션입니다.
David Rodríguez-dribeas

0

사라진 것을 말하는 것입니다. 다음은 작동합니다

#include <string>
#include <iostream>

class Sandbox
{

public:
    const string member = " "; //default to whatever is the requirement
    Sandbox(const string& n) : member(n) {}//a copy is made

};

int main()
{
    Sandbox sandbox(string("four"));
    std::cout << "The answer is: " << sandbox.member << std::endl;
    return 0;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.