move 의미론과 rvalue 참조
rvalue와 lvalue의 개념
C++에서 모든 표현식은 lvalue(left value) 또는 rvalue(right value)로 분류됩니다.
- lvalue: 메모리 주소를 가지고 있는 표현식
- rvalue: 메모리 주소가 없는 임시적인 값
int x = 10; // x는 lvalue, 10은 rvalue
int y = x; // y는 lvalue, x는 lvalue
int z = x + y; // z는 lvalue, x + y는 rvalue
rvalue 참조
rvalue 참조는 &&
를 사용하여 선언하며, rvalue에 대한 참조를 나타냅니다.
int&& rref = 10; // rvalue 참조
// int& lref = 10; // 컴파일 에러: lvalue 참조는 rvalue에 바인딩할 수 없음
std::move 함수
std::move
는 lvalue를 rvalue로 캐스팅하는 함수입니다.
#include <iostream>
#include <utility>
#include <string>
int main() {
std::string str = "Hello";
std::string&& rref = std::move(str);
std::cout << "str: " << str << std::endl; // str은 비어있을 수 있음
std::cout << "rref: " << rref << std::endl;
return 0;
}
이동 생성자와 이동 대입 연산자
이동 생성자와 이동 대입 연산자는 객체의 리소스를 효율적으로 이동시키는 데 사용됩니다.
class MyString {
private:
char* data;
public:
// 이동 생성자
MyString(MyString&& other) noexcept : data(other.data) {
other.data = nullptr;
}
// 이동 대입 연산자
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
other.data = nullptr;
}
return *this;
}
// 다른 멤버 함수들...
};
완벽한 전달 (Perfect Forwarding)
완벽한 전달은 함수 템플릿에서 인자의 값 범주(value category)를 유지하면서 다른 함수로 전달하는 기법입니다.
#include <utility>
template<typename T>
void wrapper(T&& arg) {
someFunction(std::forward<T>(arg));
}
move 의미론의 성능 이점
move 의미론은 불필요한 복사를 줄여 성능을 향상시킬 수 있습니다.
#include <vector>
#include <string>
std::vector<std::string> createVector() {
std::vector<std::string> vec;
vec.push_back("Hello");
vec.push_back("World");
return vec; // 이동 생성자 호출
}
int main() {
std::vector<std::string> myVec = createVector(); // 불필요한 복사 없음
return 0;
}
연습 문제
- rvalue 참조를 사용하여 swap 함수를 구현하세요.
std::unique_ptr
의 간단한 버전을 구현하세요. 이동 생성자와 이동 대입 연산자를 포함해야 합니다.- 다음 코드의 출력을 예측하고, 실제로 실행해 보세요. 결과에 대해 설명하세요.
#include <iostream>
#include <utility>
void f(int& x) { std::cout << "lvalue reference" << std::endl; }
void f(int&& x) { std::cout << "rvalue reference" << std::endl; }
template<typename T>
void forwarder(T&& x) {
f(std::forward<T>(x));
}
int main() {
int a = 1;
forwarder(a);
forwarder(1);
forwarder(std::move(a));
return 0;
}
참고자료
- Effective Modern C++ by Scott Meyers: Item 23-30
- C++ Move Semantics - The Complete Guide by Nicolai M. Josuttis
- C++ Reference : Move constructors and move assignment operators
- CppCon 발표 영상 : "Back to Basics: Move Semantics" by Klaus Iglberger