LR Value의 이해 - ③ std::move를 활용한 최적화
함수 인자로 전달
R-Value는 한 번 사용되고 사라진다는 특징이 있다. 그래서 R-Value로 인자를 전달받을 경우 값을 복사하지 말고 소유권을 이전받는 방식이 더욱 효율적이다. 아래의 3가지 함수를 살펴보자.
#include <string>
// value로 전달
void storeByValue(std::string s)
{
std::string b = s;
}
// L-Ref로 전달
void storeByLRef(std::string& s)
{
std::string b = s;
}
// R-Ref로 전달
void storeByRRef(std::string&& s)
{
std::string b = std::move(s);
}
int main()
{
std::string a = "abc";
storeByValue(a);
storeByLRef(a);
storeByRRef(std::move(a));
return 0;
}
위 3가지 함수를 그림으로 표현하면 다음과 같다.
- storeByValue: 두번 copy 됨
- sotreByLRef: 한번 copy 됨
- sotreByRRef: copy가 없음. 대신 a의 소유권이 b로 이전되기 때문에 main 함수의 a는
nullptr
이 됨.
std::move를 활용한 최적화
앞서 다룬 storeByRRef 함수는 0 copy이기 때문에 성능은 좋더라도 main 함수의 a
는 값을 잃어버리는 문제가 발생한다. 여기서 구현하고자 하는 것은 다음과 같다.
- 인자가 L-Value 일 경우 원본을 유지하고, 원본을 유지하기 위해서 1 copy
- 인자가 R-Value일 경우 원본을 유지할 필요가 없기 때문에 0 copy
최적화를 위해서 몇 가지 함수를 살펴보자.
Pass by value
class Cat {
public:
void setName(std::string s)
{
name = s;
}
private:
std::string name;
};
int main()
{
Cat kitty;
std::string str = "kitty";
// L-value 전달 : 2 copy
kitty.setName(str);
// R-value 전달 : 1 copy
kitty.setName("kitty");
return 0;
}
만약 위와 같이 매개변수로 R-Value를 넘겨줄 경우 컴파일러는 copy를 하지 않고 바로 s에 kitty를 넣어주는 copy elision 최적화를 진행한다. copy elision 최적화란 컴파일러는 setName의 인자가 R-Value인것을 알기 때문에 copy가 아닌 move로 s에 값을 넣어주는 최적화를 뜻한다. 그래서 name에 s를 대입하는 상황에서 한 번의 copy만 발생한다. 그러나 L-value를 전달 할 때는 copy가 불필요하게 2번이나 발생한다. 아래의 코드를 살펴보자.
Pass by Ref
class Cat {
public:
void setName(std::string& s)
{
name = s;
}
private:
std::string name;
};
int main()
{
Cat kitty;
std::string str = "kitty";
// L-value 전달 : 1 copy
kitty.setName(str);
// R-value 전달 : 에러 발생
kitty.setName("kitty");
return 0;
}
위 코드는 매개변수로 반드시 참조 가능한 L-Value를 넘겨줘야 하기 때문에 R-Value를 전달할 경우 컴파일 에러가 발생한다.
Pass by Value 그리고 std::move
앞서 나온 Pass by value에서 값을 대입할 때 std::move를 사용하는 방법이다.
class Cat {
public:
void setName(std::string s)
{
name = std::move(s);
}
private:
std::string name;
};
int main()
{
Cat kitty;
std::string str = "kitty";
// L-value 전달 : 1 copy
kitty.setName(str);
// R-value 전달 : 0 copy
kitty.setName("kitty");
return 0;
}
함수 인자로 L-value로 전달할 경우 매개변수 s
에 str
값이 복사되면서 한번 복사가 진행된다. 그리고 s
의 값을 name
이전시켜주게 되면 단 한 번의 copy로 값을 넘겨받을 수 있다.
그리고 함수 인자로 R-value를 전달할 경우 copy elision 최적화로 인해서 s는 copy 없이 값을 받을 수 있고 move를 통해서 또다시 copy 없이 name에 값을 넘겨줄 수 있다.
그리고 앞서 문제 되었던 main
함수의 str
데이터 또한 문제없이 보존할 수 있다.
출처: https://www.youtube.com/channel/UCHcG02L6TSS-StkSbqVy6Fg