프로그래밍 언어/C++

C++ r-value && (임시 객체) / l-value & (고유 객체, 주소값)

ShovelingLife 2022. 6. 15. 18:00

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));
}

출처 : https://effort4137.tistory.com/entry/Lvalue-Rvalue