-
[C++] std::move return은 정말 필요할까?쾌락없는 책임 (공부)/C++ 짜잘이 2023. 1. 22. 11:41반응형
개요
C++11부터의 중요 화두인 R-value reference를 보고 나서 '앞으로 모든 return 에 std::move를 명시해야 하는가?' 란 의문이 생기게 되었습니다. 앞으로 기존에 단순 return 하는 함수들에도 이를 써야 하나 라는 생각을 했는데 이번에 이에 대해서 알아본 결과를 한번 적어보겠습니다.
일단 return std::move() 는 불필요하다
일단 알아본 바로는 move로 반환할 이유가 없다는 것입니다.
먼저 일반적으로 반환하는 경우 컴파일러가 자동적으로 R-value로 변경을 해 줘서 이후 함수 반환값을 통해서 이동 생성자를 부를 수 있다고 합니다. 다만 std::move를 활용하게 되면 컴파일러가 이를 최적화할 여지가 사라진다고 합니다.
RVO, NRVO와 copy elision
RVO 는 Return Value Optimization
NRVO는 Named Return Value Optimization
elision은 생략위 return move와 관련한 부분을 알아보면 이런 개념들이 나오게 됩니다. 컴파일러가 알아서 반환값에 대한 최적화를 하는 것인데요. 이러한 개념으로 인해서 컴파일러가 반환값을 r-value로 반환을 해주게 되면서 move를 명시해 줄 필요가 없다는 것입니다.
이 경우를 제가 비주얼 스튜디오에서 한번 확인을 해 봤습니다.
#include <iostream> struct Foo { Foo() { std::cout << "Constructed" << std::endl; } Foo(const Foo&) { std::cout << "Copy-constructed" << std::endl; } Foo(Foo&&) { std::cout << "Move-constructed" << std::endl; } ~Foo() { std::cout << "Destructed" << std::endl; } }; Foo Func() { Foo f; std::cout << &f << '\n'; return f; } int main() { Foo f1{ Func() }; std::cout << &f1 << '\n'; return 0; }
여러가지 장난을 쳐본 결과 일단 Debug 모드에서는 move constructor가 불렸습니다. Release 모드에서는 이와 관련한 RVO, NRVO가 켜져 있다고 했는데 이 덕분인지 Foo에서는 Constructored, Destructored만 불리게 되었습니다.
보시는 것처럼 Debug 모드에서는 주소값도 다른 모습을 볼 수 있는데
Release 모드에서는 최적화를 해줘서 주소값도 그렇고 한결 간결한 모습을 볼 수 있습니다.
그럼 이번 의문인 std::move를 적용하면 어떻게 될까요?
Foo Func() { Foo f; std::cout << &f << '\n'; return std::move(f); }
둘 다 뭔가 이상한 모습을 볼 수 있습니다. 이 경우 이동 생성자를 통해서 모두 처리되는 모습을 볼 수 있습니다. 결국 RVO / NRVO 둘 다 일어나지 않은 모습을 볼 수 있습니다. 최신의 컴파일러일수록 이런 부분의 최적화는 제대로 이루어지고 있으며 함수 내 지역변수에 대해서 move를 해야 할 필요성이 점점 없습니다.
당장 비주얼 2022 버전에서 돌려보면 저렇게 쓰지 말라는 경고가 나오는걸 볼 수 있습니다.
반응형'쾌락없는 책임 (공부) > C++ 짜잘이' 카테고리의 다른 글
[C++] Branchless 가 얼마나 잘 작동할까? (0) 2023.08.23 [C++] ptr[offset], offset[ptr] 은 같은 값이다? (0) 2023.03.16 [C++] sort 함수에 함수 객체가 좋을까 함수가 좋을까? (0) 2022.09.02 [C++/OS] 메모리 단편화와 해결 방법은? (0) 2022.08.28 [C++] vector<bool> 보기를 돌같이 해라 (0) 2022.08.16