컴파일러의 컴파일 수준 단계에 따라 여러 최적화가 들어가는데, 오늘 페이스북에서 윤훈남님이 올려주신 글이 신기해 조금 알아보았다.

복사, 이동 생성자를 무복사 값에 의한 전달로 최적화 하는 것인데, 결론적으로는 기본 생성자를 부르는 것 정도인듯 하다.

복사/이동 생성자 및 소멸자가 특정 행동을 하도록 만들어져 있다고 하더라도 생략된다고 한다.

#include <iostream>
#include <type_traits>
#include <concepts>

struct Weird {
	Weird() { std::cout << "#1\n"; }
	Weird(Weird&) { std::cout << "#2\n"; }
};

Weird g() {
	Weird w;
	return w;
}

int main() {
	Weird m1 = g();
}
Debug 모드일 때
Release 모드일 때 (#2가 호출되어야할 복사 생성자가 호출되지 않았다.)

작동되는 상황

  • If a function returns a class type by value, and the return statement‘s expression is the name of a non-volatile object with automatic storage duration, which isn’t the function parameter, or a catch clause parameter, and which has the same cv-unqualified type as the return type of the function, then copy/move is omitted. When that local variable is constructed, it is constructed directly in the storage where the function’s return value would otherwise be moved or copied to. This variant of copy elision is known as NRVO, “named return value optimization”.
  • When a nameless temporary, not bound to any references, would be moved or copied into an object of the same cv-unqualified type, the copy/move is omitted. When that temporary is constructed, it is constructed directly in the storage where it would otherwise be moved or copied to. When the nameless temporary is the argument of a return statement, this variant of copy elision is known as RVO, “return value optimization”.

C++ 11 이후 추가 스펙

  • In a throw-expression, if the operand is the name of a non-volatile object with automatic storage duration, which isn’t the function parameter, or a catch clause parameter, and whose scope does not extend past the innermost try-block (if there is a try-block), then copy/move is omitted. When that local variable is constructed, it is constructed directly in the storage where the exception object would otherwise be moved or copied to.
  • When handling an exception, if the argument of the catch clause is of the same type (except for cv-qualification) as the exception object thrown, the copy is omitted and the body of the catch clause accesses the exception object directly, as if caught by reference. This is disabled if such copy elision would change the observable behavior of the program for any reason other than skipping copy constructor and the destructor of the catch clause’s parameter.

https://ko.cppreference.com/w/cpp/language/copy_elision