M.4 std::move

주홍영·2022년 3월 22일
0

Learncpp.com

목록 보기
194/199

https://www.learncpp.com/cpp-tutorial/stdmove/

우리가 move semantics를 더욱 자주 사용하게 되면
우리는 l-value인 경우에 move semantics를 적용하고 싶을 수 있다

#include <iostream>
#include <string>

template<class T>
void myswap(T& a, T& b)
{
  T tmp { a }; // invokes copy constructor
  a = b; // invokes copy assignment
  b = tmp; // invokes copy assignment
}

int main()
{
	std::string x{ "abc" };
	std::string y{ "de" };

	std::cout << "x: " << x << '\n';
	std::cout << "y: " << y << '\n';

	myswap(x, y);

	std::cout << "x: " << x << '\n';
	std::cout << "y: " << y << '\n';

	return 0;
}

위와 같은 swap 을 진행할 때
myswap함수에서는 세번의 copy가 일어난다

우리가 지난 lesson을 통해 알고 있듯이 copy는 비효율적이다
그리고 예시로 본 swap은 세번의 copy를 사용하고 있다
이는 과한 string copy와 destruct를 유발하고 이는 프로그램을 느리게 만든다

그러나 copy가 여기서 필요한 것은 아니다. 우리가 필요한 것은 a와 b의 value를 swap 하는 것 뿐이다. 따라서 우리는 3번의 move만으로도 이러한 목적을 달성할 수 있다. 그리고 이는 copy를 사용할 때보다 훨씬 효율적이다

그렇지만 어떻게 이를 구현할까? 여기서 문제는 모두 l-value라는 것이다
r-value ref가 아닌 l-value이므로 우리는 move cosntructor나 move assignment를 호출할 수 없다. 그렇다면 어떻게 해야할까?

std::move

c++11에서는 std::move라는 standar library function이 있다
이는 static_cast를 이용해 argument를 r-value reference로 casting해준다
따라서 move semantics가 호출되도록 만들어 준다
그러무로 우리는 std::move함수를 이용해 l-value를 가지고도 move consturctor, assignment를 호출할 수 있다. 참고로 std::move는 utility header에 정의되어 있다

이는 std::move를 사용한 똑같은 예시이다

#include <iostream>
#include <string>
#include <utility> // for std::move

template<class T>
void myswap(T& a, T& b)
{
  T tmp { std::move(a) }; // invokes move constructor
  a = std::move(b); // invokes move assignment
  b = std::move(tmp); // invokes move assignment
}

int main()
{
	std::string x{ "abc" };
	std::string y{ "de" };

	std::cout << "x: " << x << '\n';
	std::cout << "y: " << y << '\n';

	myswap(x, y);

	std::cout << "x: " << x << '\n';
	std::cout << "y: " << y << '\n';

	return 0;
}

앞선 예시와 다르게 myswap안에서는 3번의 move semantics가 호출됨을 알 수 있다

copy를 만들기 전에 move를 통해서 더욱 효율적인 프로그램이 되었다

Another example

우리는 std::move를 이용해서 std::vector와 같은 container를 l-value를 이용해 move semantics를 적용할 수 있다

다음의 예시는 copy semantics와 move semantics 모두가 존재한다

#include <iostream>
#include <string>
#include <utility> // for std::move
#include <vector>

int main()
{
	std::vector<std::string> v;
	std::string str = "Knock";

	std::cout << "Copying str\n";
	v.push_back(str); // calls l-value version of push_back, which copies str into the array element

	std::cout << "str: " << str << '\n';
	std::cout << "vector: " << v[0] << '\n';

	std::cout << "\nMoving str\n";

	v.push_back(std::move(str)); // calls r-value version of push_back, which moves str into the array element

	std::cout << "str: " << str << '\n';
	std::cout << "vector:" << v[0] << ' ' << v[1] << '\n';

	return 0;
}

Where else is std::move useful?

많은 sorting algorithm은 swaping을 사용하게 된다
이럴 때 std::move를 이용한 move semantics를 적용하면 더욱 효율적이다

그리고 또한 smart pointer로 관리되고 있는 contents를 옮기고 싶을 때도 유용하다.

Conclusion

std::move는 l-value를 r-value처럼 다루고 싶을 때 언제든지 사용할 수 있다
이는 보통 move semantics를 호출하기 위함이 목적이다

profile
청룡동거주민

0개의 댓글