공부/C || C++

C++ std::move

sudo 2022. 9. 5. 17:24

C++ 11에서 Rvalue reference를 공부하다보니 처음보는 개념들이 너무 많이 쏟아져 나온다. 그중 하나가 std::move이다.

기능은 쉽게 이야기해서 lvalue를 rvalue로 전환해주는 것이 전부이다. 다만 B = std::move(A)를 한다면, A는 아무런 값도 갖지 않은 객체가 된다는 것을 인지해야한다. 이름이 move라고 해서 값을 옮겨준다거나 그렇게 하진 않는다. std::move는 주로 move 생성자에 많이 쓰인다.

 

아래 코드를 보면 왜 move 생성자에서 자주 쓰이는지 알 수 있다. (코드 출처 : https://openmynotepad.tistory.com/10)

class Item {
public:

 Item(const int _n) : m_nx(_n) { cout << "일반 생성자 호출" << endl; }

 Item(const Item& rhs) : m_nx(rhs.m_nx) { cout << "복사 생성자 호출" << endl; }

 Item(const Item&& rhs) : m_nx(std::move(rhs.m_nx)) { cout << "이동 생성자 호출" << endl; }

 ~Item() { cout << "소멸자 호출" << endl; }
 
 private:
 int m_nx;
};

move 생성자에서 std::move를 사용한 것을 확인할 수 있다. rhs의 m_nx멤버를 lvalue에서 rvalue로 전환해주고 있는데 

 

사실 이전의 rvalue reference글에서의 move 생성자도 이렇게 써도 똑같다.

// Move 생성자
MemoryBlock(MemoryBlock&& other) : _data(NULL), _length(0)
{
	std::cout << "Move Constructor Length : " << other._length << std::endl;
	*this = std::move(other); 
    /* 원래는 위의 한줄을 아래와 같이 4줄에 걸쳐서 썼음 */
    /*
    _data = other._data;
    _length = other._length;
    other._data = NULL;
    other._length = 0;
    */
}

 

std::move에 대해 더 이해가 잘되는 예시 코드를 써주신 분이 있어서 가져와봤다

void func(const Widget& rhs) 
{
    cout << "const Widget&" << endl;
}

void func(Widget&& rhs)
{
    cout << "Widget&& " << endl;
}

int main() 
{
    Widget A;
    func(std::move(A));    // 이제 func(Widget&& rhs) 버전이 호출됨
    func(static_cast<Widget&&>(A));    // std::move 와 같음
}

 

더 이상 lvalue로써의 참조가 필요 없을 때 std::move(A)를 사용함으로써 소유권을 이전할 수 있다. 결과적으로 Move Constructor가 호출될 것 이다. 이는 static_cast로 Widget&& 타입으로 캐스팅한 것과 같은 결과를 가져온다.

 

 

Reference:

https://openmynotepad.tistory.com/10

 

emplace_back 과 push_back 의 차이

item 타입의 생성자가 타입을 인자로 받는다면? push_back 함수는 '객체' 를 집어 넣는 형식으로, 객체가 없이 삽입을 하려면 "임시객체 (rvalue) " 가 있어야 합니다. 또는 암시적 형변환이 가능하다면,

openmynotepad.tistory.com

https://openmynotepad.tistory.com/21?category=853099