C++ Lvalue와 Rvalue에 대한 오해
Lvalue와 Rvalue는 보통 Left-value(왼쪽값)과 Right-value(오른쪽값)로 풀어서 쓴다. C 표준에서는 대입 연산자(=)를 기준으로 왼쪽과 오른쪽에 모두 사용될 수 있는 값은 Lvalue이고 오른쪽에만 사용될 수 있는 값이 Rvalue라고 정의하고 있지만 C++ 관점에서는 전혀 다른 관점에서 해석할 필요가 있다.
Lvalue와 Rvalue의 구분
C++에서 모든 표현식은 Lvalue 또는 Rvalue이다. Lvalue는 단일 표현식 이후에도 없어지지 않고 지속되는 객체이다. 쉽게 생각해서 이름을 가지는 객체는 Lvalue라고 얘기할 수 있다. 그러므로 const 타입을 포함한 모든 변수는 Lvalue 이다. 반면에 Rvalue는 표현식이 종료된 이후에는 더이상 존재하지 않는 임시적인 값이다.
++x는 Lvalue인 반면에 x++은 RValue라는 점이다. 둘 다 증가된 값을 리턴하지만 ++x는 증가된 x 자신을 리턴하기 때문에 Lvalue인 반면에 x++은 증가된 복사본을 리턴하기 때문에 Rvalue 이다. & 연산자는 Lvalue를 요구하기 때문에 표현식이 Rvalue라면 컴파일 오류가 나게 된다.
&(++x);
&(x++); // error C2102: '&' requires l-value
#include <iostream>
#include <format>
#include <vector>
using namespace std;
#define SAFE_DELETE(x) if(x != nullptr) delete x;
class Test
{
public:
int x = 0, y = 0;
public:
static Test&& Create_tmp_val()
{
return Test();
}
public:
Test()
{
cout << "기본 생성자" << endl;
}
Test(int _x, int _y) : x(_x), y(_y)
{
}
Test(const Test& _ref)
{
cout << "복사 생성자 l-value" << endl;
}
Test(Test&& _ref) noexcept : Test(_ref.x, _ref.y)
{
cout << "복사 생성자 r-value" << endl;
}
Test& operator=(const Test& _ref)
{
cout << "할당 연산자 오버로딩 l-value" << endl;
this->x = _ref.x;
this->y = _ref.y;
return *this;
}
Test& operator=(Test&& _ref) noexcept
{
cout << "할당 연산자 오버로딩 r-value" << endl;
this->x = _ref.x;
this->y = _ref.y;
return *this;
}
~Test()
{
cout << "기본 소멸자" << endl;
}
public:
void Print()
{
cout << format("p3의 값 / x : {}, y : {} \n", x, y);
}
};
int main()
{
cout << "클래스 포인터 변수 테스트" << endl;
Test* p1 = new Test();
Test* p2 = new Test(*p1);
auto val = Test::Create_tmp_val();
p2 = new Test(val);
SAFE_DELETE(p1);
SAFE_DELETE(p2);
cout << "\n클래스 변수 테스트" << endl;
Test p3(3, 4), p4(6,7);
p3.Print();
p3 = p4;
p3.Print();
p3 = Test(4, 4);
p3.Print();
cout << endl;
cout << "유니크 클래스 포인터 변수 테스트" << endl;
unique_ptr<Test> up_test = make_unique<Test>();
if (auto p_test = up_test.get())
{
*p_test = Test(7, 7);
p_test->Print();
}
cout << "\n벡터 변수 테스트" << endl;
vector<Test> vec_test;
vec_test.push_back(Test(6, 5));
vec_test.insert(vec_test.begin(), Test(7, 7));
}
'프로그래밍 언어 > C++' 카테고리의 다른 글
C++ 전방 선언 (Forward Declaration) (0) | 2022.06.16 |
---|---|
C++ typename의 두 가지 의미 (0) | 2022.06.16 |
C++ RTTI 그리고 vtable(가상 함수 테이블) (0) | 2022.06.14 |
C++ 출력(std::format)과 for-range loop 꿀팁 (0) | 2022.06.14 |
C++ RVO NRVO 반환값 최적화 / Copy Elision(복사 생략) (0) | 2022.06.14 |