우리가 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를 호출할 수 없다. 그렇다면 어떻게 해야할까?
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를 통해서 더욱 효율적인 프로그램이 되었다
우리는 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;
}
많은 sorting algorithm은 swaping을 사용하게 된다
이럴 때 std::move를 이용한 move semantics를 적용하면 더욱 효율적이다
그리고 또한 smart pointer로 관리되고 있는 contents를 옮기고 싶을 때도 유용하다.
std::move는 l-value를 r-value처럼 다루고 싶을 때 언제든지 사용할 수 있다
이는 보통 move semantics를 호출하기 위함이 목적이다