std::move가 단지 캐스팅일 뿐이라니?

일단, move 구현을 보자

template<typename _Tp> inline typename std::remove_reference<_Tp>::type&& move(_Tp&& __t) { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

move가 무엇을 하는지 자세히 보니,

static_cast<typename std::remove_reference<_Tp>::type&&>(__t);

진짜 캐스팅뿐이다. 그렇다. source object가 없어지거나 그런거 아니다.

뭐 값을 대입시킬 객체에는 rvalue reference를 넘겨서 메모리주소를 그대로 전달한다고 치고,


이시점에서 궁금한건 오로지하나,

"뭐지? 그럼 기존 객체는 어떻게 없어지는거야?"

알고보니, 그 답은 rvalue reference 생성자와 대입연산자에 있었다.

rvalue reference 생성자와 대입연산자에서는 source object의 메모리 주소를 dest object에 주고

source object는 nullptr처리하거나 하는것이다.

대표적인 예가 std::string 이다.

assign(basic_string&& __str) { this->swap(__str); return *this; }

보면, source와 dest를 swap하고 있다. 따라서 string을 move시켜보면 move당한 놈은 "" 빈스트링을 갖게된다.

그럼 한번 테스트해볼까? 내가 만든 클레스를 move시킨면 어떻게되는지?

repl.it: https://repl.it/FbVT/0

code:

#include <iostream>
#include <stdlib.h>

class TestMe {

public:
TestMe(){ i = nullptr; }
void set(int& k){ i = &k; }
int get(){ return *i; }
void plus(){ (*i)++; }

private:
int* i;
};

int main()
{
        int test1 = 1;
        TestMe t1;
        t1.set(test1);

TestMe t2(std::move(t1));

t2.plus();
std::cout << "t1: " << t1.get() << std::endl;
std::cout << "t2: " << t2.get() << std::endl;

t1.plus();
std::cout << "t1: " << t1.get() << std::endl;
std::cout << "t2: " << t2.get() << std::endl;   

   return 0;

result:

 t1: 2
 t2: 2
 t1: 3
 t2: 3

아마도 이 결과로부터 유추할수있는건, 

std string처럼 swap같은건 없고 메모리는 일단 복사가되었는데 source object에 대한 후처리는 없다는 것이다. 

따라서 default move constructor가 어떻게 작동하는지 이해하고, 맘에 안든다면 직접 구현해야한다.



References

1) std::string source

2) std::remove_reference

3) What are rvalues, lvalues, xvalues, glvalues, and prvalues?


블로그 이미지

시간을 거스르는자

,