[R value] Copy Elision RVO NRVO - ChoiChiWon/ccw GitHub Wiki
copy elision
- 컴파일러가 불필요한 생성자 호출을 제외시켜 주는 컴파일러 최적화 옵션이다.
- C++ 표준에서는 언어 표준이 요구한대로 구현된다면 어떤 최적화를 해도 좋다고하고 있다.
- 하지만 프로그램의 동작이 변경 될 수 있음에도 불구하고, 여전히 복사가 생략되는 상황이 몇 가지 있다.
- 대표적인 것이 반환 값 최적화이며, 다른 하나는 클래스 타입의 임시 객체가 동일한 타입의 개체로 복사 할 때의 최적화이다.
int n = 0;
struct C {
explicit C(int) {}
C(const C&) { ++n; } // the copy constructor has a visible side effect
}; // it modifies an object with static storage duration
int main() {
C c1(42); // direct-initialization, calls C::C(42)
C c2 = C(42); // copy-initialization, calls C::C( C(42) )
std::cout << n << std::endl; // prints 0 if the copy was elided, 1 otherwise
return 0;
}
// result :
// 0
c++17에서의 Guaranteed Copy Elision
- 이동이 불가능한 타입에 대해서는 Copy Elision이 적용되지 않았다.
- 결과적으로, 내용상 Copy Elision이 가능함에도 이동이 불가능한 타입에 대해서 컴파일 에러 발생하는 문제가 있었다.
- C++17에서는 Non-movable 객체에 대해서도 Copy Elision이 정상 동작함을 보장하는 것이다.
- 이것이 C++17의 "Guaranteed Copy Elision"의 핵심적인 내용이다.
struct Foo
{
Foo() { std::cout << "Constructed\n"; }
Foo(const Foo &) = delete;
Foo(const Foo &&) = delete;
~Foo() { std::cout << "Destructed\n"; }
};
Foo f()
{
// 컴파일 에러
// error C2280: 'Foo::Foo(const Foo &&)': 삭제된 함수를 참조하려고 합니다.
return Foo();
}
int main()
{
Foo foo = f();
}
RVO / NRVO(Named RVO)
- RVO/NRVO는 함수의 반환값이 특정 객체의 값 형식일 때 복사 생성을 회피할 수 있도록 컴파일러 최적화를 의미한다.
- 즉 임시 객체 생성을 하지 않는다.
- NRVO는 이름이 있는 변수에 대해서도 RVO가 적용되는 것이다.
- vistual studio 2005 부터 지원한다.
- RVO와는 다르게 NRVO는 최적화 옵션 /O1(크기 최소화)부터 동작한다.
-- Debug 모드에서는 NRVO가 작동하지 않는다.
-- RVO와 다르게 변수를 선언하고, 해당 변수를 사용한 다음 반환해도 임시 객체가 생성되지 않는다.
Named Return Value Optimization in Visual C++ 2005 - microsoft
class RVO
{
public:
RVO(){printf("I am in constructor\n");}
RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
~RVO(){printf ("I am in destructor\n");}
int mem_var;
};
RVO MyMethod (int i)
{
RVO rvo;
rvo.mem_var = i;
return (rvo);
}
int main()
{
RVO rvo;
rvo=MyMethod(5);
}
Without NRVO (cl /Od sample1.cpp), the expected output would be:
I am in constructor
I am in constructor
I am in copy constructor
I am in destructor
I am in destructor
I am in destructor
With NRVO (cl /O2 sample1.cpp), the expected output would be:
I am in constructor
I am in constructor
I am in destructor
I am in destructor
RVO 가 적용되지 않는 경우
- 조건에 의해 반환값이 다른 경우
Foo f()
{
Foo a;
if (true)
return a;
else
return nullptr;
}
- std::move를 통해서 객체를 반환하려는 경우
-- 이동 생성자가 구현되어 있다면 이동 생성자가 호출된다.
Foo f()
{
Foo a;
return std::move(a);
}
- static 선언
-- 정적 선언 같은 경우 data 영역 메모리에 공간을 잡고 반환 메모리 공간에 복사하기 때문에 복사 생성자가 호출된다.
Foo f()
{
static Foo a;
return a;
}
참고 사이트
What are copy elision and return value optimization? - stackoverflow
RVO Setup and Mechanism
RVO V.S. std::move