참고 :이 답변은 C ++ 11 이후에만 적용됩니다. "C / C ++"와 같은 것은 없으며 서로 다른 언어입니다.
아니요, 로컬 개체를 값으로 반환하는 것은 위험하지 않으므로 그렇게하는 것이 좋습니다. 그러나 여기에 모든 답변에서 빠진 중요한 점이 있다고 생각합니다. 다른 많은 사람들은 구조체가 RVO를 사용하여 복사되거나 직접 배치되고 있다고 말했습니다. 그러나 이것은 완전히 정확하지 않습니다. 나는 지역 객체를 반환 할 때 어떤 일이 일어날 수 있는지 정확하게 설명하려고 노력할 것입니다.
의미론 이동
C ++ 11 이후로 안전하게 훔칠 수있는 임시 객체에 대한 참조 인 rvalue 참조가 있습니다. 예를 들어 std :: vector에는 이동 할당 연산자와 함께 이동 생성자가 있습니다. 둘 다 일정한 복잡도를 가지며 단순히 이동되는 벡터의 데이터에 대한 포인터를 복사합니다. 여기서는 이동 의미론에 대해 자세히 설명하지 않겠습니다.
함수 내에서 로컬로 생성 된 객체는 임시적이고 함수가 반환 될 때 범위를 벗어나기 때문에 반환 된 객체는 C ++ 11 이후부터 복사 되지 않습니다 . 반환되는 객체에서 이동 생성자가 호출됩니다 (또는 나중에 설명 됨). 즉, 값 비싼 복사 생성자를 사용하여 객체를 반환하지만 큰 벡터와 같이 저렴한 이동 생성자를 사용하면 데이터의 소유권 만 로컬 객체에서 반환 된 객체로 전송되므로 저렴합니다.
특정 예에서는 개체 복사와 이동간에 차이가 없습니다. 구조체의 기본 이동 및 복사 생성자는 동일한 작업을 수행합니다. 두 개의 정수를 복사합니다. 그러나 이것은 전체 구조체가 64 비트 CPU 레지스터에 맞기 때문에 적어도 다른 솔루션보다 빠릅니다. (내가 틀리면 CPU 레지스터를 많이 알지 못합니다).
RVO 및 NRVO
RVO는 반환 값 최적화를 의미하며 컴파일러가 수행하는 몇 안되는 최적화 중 하나이며 부작용이있을 수 있습니다. C ++ 17부터 RVO가 필요합니다. 이름이 지정되지 않은 개체를 반환 할 때 호출자가 반환 된 값을 할당하는 제자리에서 직접 생성됩니다. 복사 생성자 또는 이동 생성자가 호출되지 않습니다. RVO가 없으면 이름이 지정되지 않은 객체가 먼저 로컬에서 생성 된 다음 반환 된 주소에서 생성 된 다음 이동하면 이름이 지정되지 않은 로컬 객체가 파괴됩니다.
RVO가 필요하거나 (c ++ 17) 가능성이있는 예 (c ++ 17 이전) :
auto function(int a, int b) -> MyStruct {
return MyStruct{a, b};
}
NRVO는 명명 된 반환 값 최적화를 의미하며 호출 된 함수에 로컬 인 명명 된 개체에 대해 수행된다는 점을 제외하면 RVO와 동일합니다. 이것은 여전히 표준 (c ++ 20)에 의해 보장되지 않지만 많은 컴파일러가 여전히 그렇게합니다. 명명 된 로컬 개체를 사용하더라도 반환 될 때 최악의 경우 이동됩니다.
결론
값으로 반환하지 않는 것을 고려해야하는 유일한 경우는 명명 된 매우 큰 (스택 크기에서와 같이) 객체가있는 경우입니다. 이는 NRVO가 아직 보장되지 않았기 때문입니다 (c ++ 20 기준). 객체 이동조차 느려질 수 있기 때문입니다. 내 권장 사항과 Cpp 핵심 지침 의 권장 사항은 항상 값으로 개체를 반환하는 것을 선호하는 것입니다 (복수 값이 여러 개인 경우 구조체 (또는 튜플) 사용). 유일한 예외는 개체를 이동하는 데 비용이 많이 드는 경우입니다. 이 경우 상수가 아닌 참조 매개 변수를 사용하십시오.
C ++의 함수에서 수동으로 해제해야하는 리소스를 반환하는 것은 결코 좋은 생각이 아닙니다. 그렇게하지 마십시오. 최소한 std :: unique_ptr을 사용하거나 리소스 ( RAII ) 를 해제하고 그 인스턴스를 반환하는 소멸자로 자신의 비 로컬 또는 로컬 구조체를 만드십시오 . 리소스에 자체 이동 의미가없는 경우 이동 생성자와 이동 할당 연산자를 정의하는 것도 좋은 생각입니다 (복사 생성자 / 할당 삭제).