로컬 변수가 범위를 벗어나기 전에 반환 값이 복사됩니다. 복사/이동은 임시 위치 (스택 또는 레지스터) 또는 호출자 자신의 버퍼 또는 기본 레지스터에 직접 적용될 수 있습니다 - 최적화/인라인 문제입니다.
임시 위치와 관련하여 컴파일러는 호출자와 호출 수신자간에 작업 단위를 정렬해야하며 반환 값 (물론 함수 매개 변수)에 대해 여러 가지 OS/2 진 오브젝트/실행 가능 형식 특정 규칙이 있습니다. 하나의 컴파일러로 컴파일 된 라이브러리/객체는 일반적으로 다른 컴파일러와 함께 사용할 수 있습니다.
라인은 ...
auto item = q.pop();
은 ... 강력 예외 안전 할 것인가? 임시 위치 함수의 리턴 값이 후 다시 호출자 버퍼에 복사되는 반환된다
는 pop_front()
수없는 throw
가정 흥미로운 경우이다. 당신이 그것에 대해 적절하게 보호하지 못했던 것 같습니다. Elision (호출자의 결과 버퍼/레지스터에서 반환 값을 직접 작성하는 호출 수신자)은 허용되지만 필수는 아닙니다.
이 문제를 탐구하기 위해, 나는 다음과 같은 코드를 작성했습니다 :
#include <iostream>
struct X
{
X() { std::cout << "X::X(this " << (void*)this << ")\n"; }
X(const X& rhs) { std::cout << "X::X(const X&, " << (void*)&rhs
<< ", this " << (void*)this << ")\n"; }
~X() { std::cout << "X::~X(this " << (void*)this << ")\n"; }
X& operator=(const X& rhs)
{ std::cout << "X::operator=(const X& " << (void*)&rhs
<< ", this " << (void*)this << ")\n"; return *this; }
};
struct Y
{
Y() { std::cout << "Y::Y(this " << (void*)this << ")\n"; }
~Y() { std::cout << "Y::~Y(this " << (void*)this << ")\n"; }
};
X f()
{
Y y;
std::cout << "f() creating an X...\n";
X x;
std::cout << "f() return x...\n";
return x;
};
int main()
{
std::cout << "creating X in main...\n";
X x;
std::cout << "x = f(); main...\n";
x = f();
}
g++ -fno-elide-constructors
로 컴파일을, (추가 의견) 내 출력했다 :
creating X in main...
X::X(this 0x22cd50)
x = f(); main...
Y::Y(this 0x22cc90)
f() creating an X...
X::X(this 0x22cc80)
f() return x...
X::X(const X&, 0x22cc80, this 0x22cd40) // copy-construct temporary
X::~X(this 0x22cc80) // f-local x leaves scope
Y::~Y(this 0x22cc90)
X::operator=(const X& 0x22cd40, this 0x22cd50) // from temporary to main's x
X::~X(this 0x22cd40)
X::~X(this 0x22cd50)
분명히 할당이 f()
후 발생 왼쪽 범위 : 스코프 가드 (여기에서는 Y로 표시)가 파괴 된 이후에 예외가 발생합니다.
메인에 X x = f();
또는 X x(f());
이 포함되어있는 경우 동일한 종류가 발생합니다. 단, f()
로컬 변수가 파기 된 후에 호출되는 복사 생성자는 예외입니다.
(나는 하나의 컴파일러의 동작이 표준에 의해 작동하도록 요구되는지 여부를 판단하기에는 좋지 않은 근거이지만, 다른 방법으로는 훨씬 더 안정적이라고 생각합니다. 컴파일러가 고장난 경우, 비교적 희귀 한 - 또는 표준은 그것을 필요로하지 않습니다. 여기에서, 컴파일러의 행동은 방금 표준 요구 사항에 대한 내 인상에 일화 가중치를 추가하는 데 사용됩니다.) 호기심에 대한
가로장 설치 등등 세부 정보 :하지 const
참조 임시의 수명을 연장, 그것은 일반적으로 한 방향으로 만 호출 할 수있는 코드가 유용하지만 는 안전 할 수있는 일이 const X& x = f();
이다, 그러나 표준 에에 임시 복사본이있는 임시 사본을 임시로 추가해야한다고 확신 할 수는 없습니다. 조금이라도 가치가있다면 - 내 프로그램에서 "효과가있다"는 것이고 흥미롭게도 임시 값은 반환 값을 추출 할 때 사용되는 동일한 스택 위치를 차지하므로 f()
코드가 효과적으로 추출되어 -f-no-elide-constructors
옵션이 너무 많이 비활성화되지는 않는다 비관주의를 추가하는 방식에서 벗어나는 최적화 : 함수를 호출하기 전에 임시 스택 공간을 남겨두고 함수를 호출하기 위해 여분의 코드를 추가하고 임시 포인터를 제거한 다음 스택 포인터를 다시 조정하십시오.
예, 자동 저장 기간 (예 :'가드 '와 같은)이있는 변수는 반환 지침 (및 구문/복사본) 이후에 삭제됩니다. 점프 지침 및 선언에서 표준 부품 6.6 및 6.7을 확인할 수 있습니다. 어쩌면 더 쉽게 더미 오브젝트로 파괴를 시도해 볼 수 있습니다 ^^ – lip