icon안동민 개발노트

move 의미론과 rvalue 참조


rvalue와 lvalue의 개념

 C++에서 모든 표현식은 lvalue(left value) 또는 rvalue(right value)로 분류됩니다.

  • lvalue: 메모리 주소를 가지고 있는 표현식
  • rvalue: 메모리 주소가 없는 임시적인 값
예제
int x = 10;  // x는 lvalue, 10은 rvalue
int y = x;   // y는 lvalue, x는 lvalue
int z = x + y;  // z는 lvalue, x + y는 rvalue

rvalue 참조

 rvalue 참조는 &&를 사용하여 선언하며, rvalue에 대한 참조를 나타냅니다.

예제
int&& rref = 10;  // rvalue 참조
// int& lref = 10;  // 컴파일 에러: lvalue 참조는 rvalue에 바인딩할 수 없음

std::move 함수

 std::move는 lvalue를 rvalue로 캐스팅하는 함수입니다.

예제
#include <iostream>
#include <utility>
#include <string>
 
int main() {
    std::string str = "Hello";
    std::string&& rref = std::move(str);
    
    std::cout << "str: " << str << std::endl;  // str은 비어있을 수 있음
    std::cout << "rref: " << rref << std::endl;
    
    return 0;
}

이동 생성자와 이동 대입 연산자

 이동 생성자와 이동 대입 연산자는 객체의 리소스를 효율적으로 이동시키는 데 사용됩니다.

예제
class MyString {
private:
    char* data;
 
public:
    // 이동 생성자
    MyString(MyString&& other) noexcept : data(other.data) {
        other.data = nullptr;
    }
 
    // 이동 대입 연산자
    MyString& operator=(MyString&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            other.data = nullptr;
        }
        return *this;
    }
 
    // 다른 멤버 함수들...
};

완벽한 전달 (Perfect Forwarding)

 완벽한 전달은 함수 템플릿에서 인자의 값 범주(value category)를 유지하면서 다른 함수로 전달하는 기법입니다.

예제
#include <utility>
 
template<typename T>
void wrapper(T&& arg) {
    someFunction(std::forward<T>(arg));
}

move 의미론의 성능 이점

 move 의미론은 불필요한 복사를 줄여 성능을 향상시킬 수 있습니다.

예제
#include <vector>
#include <string>
 
std::vector<std::string> createVector() {
    std::vector<std::string> vec;
    vec.push_back("Hello");
    vec.push_back("World");
    return vec;  // 이동 생성자 호출
}
 
int main() {
    std::vector<std::string> myVec = createVector();  // 불필요한 복사 없음
    return 0;
}

연습 문제

  1. rvalue 참조를 사용하여 swap 함수를 구현하세요.
  2. std::unique_ptr의 간단한 버전을 구현하세요. 이동 생성자와 이동 대입 연산자를 포함해야 합니다.
  3. 다음 코드의 출력을 예측하고, 실제로 실행해 보세요. 결과에 대해 설명하세요.
#include <iostream>
#include <utility>
 
void f(int& x) { std::cout << "lvalue reference" << std::endl; }
void f(int&& x) { std::cout << "rvalue reference" << std::endl; }
 
template<typename T>
void forwarder(T&& x) {
    f(std::forward<T>(x));
}
 
int main() {
    int a = 1;
    forwarder(a);
    forwarder(1);
    forwarder(std::move(a));
    return 0;
}

 참고자료

  • Effective Modern C++ by Scott Meyers: Item 23-30
  • C++ Move Semantics - The Complete Guide by Nicolai M. Josuttis
  • C++ Reference : Move constructors and move assignment operators
  • CppCon 발표 영상 : "Back to Basics: Move Semantics" by Klaus Iglberger