Home [C++] lvalue와 rvalue
Post
Cancel

[C++] lvalue와 rvalue

이 글은 현재 작성 중입니다.

• 값 범주의 종류 •

용어 설명

                    +--------------+
                    |  expression  |
                    +--------------+
                            |
                  +---------+---------+
                  ↓                   ↓
            +-----------+       +-----------+
            |  glvalue  |       |  rvalue   |
            +-----------+       +-----------+
                  |                   |
        +---------+------+     +------+---------+
        ↓                ↓     ↓                ↓
  +-----------+       +-----------+       +-----------+
  |  lvalue   |       |   xvalue  |       |  prvalue  |
  +-----------+       +-----------+       +-----------+


아래는 MS 공식 문서에 작성된 값 범주에 대한 C++17에서의 정의이다.

  • glvalue: 평가에서 개체, 비트 필드 또는 함수의 ID를 결정하는 식
  • prvalue: 계산에서 개체 또는 비트 필드를 초기화하거나 표시되는 컨텍스트에 지정된 대로 연산자의 피연산자 값을 계산하는 식
  • xvalue: 리소스를 재사용할 수 있는 개체 또는 비트 필드를 나타내는 glvalue
  • lvalue: xvalue가 아닌 glvalue
  • rvalue: prvalue 또는 xvalue

난해하다. 우리가 일반적으로 자주 사용하는 용어인 lvalue와 rvalue에 대한 직접적인 정의는 없고, glvalue, prvalue, xvalue를 이용해 정의되어 있음을 볼 수 있다. 조금 더 풀어서 설명해보자면 아래와 같다.

  • glvalue: 주소값을 갖는 식. 일반적으로 생각하는 lvalue와 유사하나, xvalue를 포함하는 개념이다. 일반적인 변수, 배열 원소, 함수 이름, 문자열 리터럴 등이 이에 속한다.
  • prvalue: 주소값을 갖지 않고, 값 자체를 주는 식. 일반적으로 생각하는 rvalue와 유사하나, xvalue를 포함하지 않는 개념이다. 기본 타입의 리터럴 등이 이에 속한다.
  • xvalue: glvalue 중 곧 소멸하여 자원을 재사용할 수 있는 식. std::move()의 결과 등이 이에 속한다.

주의사항

값 범주에 대해 공부할 때 주의해야 할 점은, 이는 표현식에 대한 개념이라는 것이다.

  • 문맥과 함께 작성된 표현식은 항상 ‘타입’과 ‘값 범주’를 함께 갖는다. 즉, 어떤 표현식의 값 범주를 판단할 때 타입과 혼동하면 안 된다.
  • 함수 이름이 glvalue에 속한다고 작성했으나, 이는 함수 이름이 함수 그 자체를 가리키는 glvalue라는 의미이며, 함수의 호출식과는 구분해야 한다. 함수의 호출식은 그 함수의 반환값에 따라 값 범주가 결정된다.

설명만으로는 감이 잘 오지 않으므로, 예시 코드를 보며 자세히 확인해보겠다.


• lvalue의 예시 •

이름 붙은 변수, 대입식

1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;

int main() {
    int a = 10, b = 20;
    cout << &a << '\n'; //0000007784EFFB34
    (a = b) = 99;
    cout << a << '\n'; //99
    cout << &a << ' ' << &(a = 1) << '\n'; //0000007784EFFB34 0000007784EFFB34
}
  • a는 lvalue로, 주소 연산자(&)를 사용해 주소값을 얻을 수 있다.
  • 대입식 (a = b)은 다시 a를 가리키는 lvalue이다. 그 자체에 값을 할당할 수 있다.
  • 대입식 (a = b)은 lvalue이므로 a와 마찬가지로 주소 연산자(&)를 사용해 주소값을 얻을 수 있다. 또한 그 주소값은 a와 동일하다.

배열 원소, 포인터 역참조

1
2
3
4
5
6
7
8
9
#include <iostream>
using namespace std;

int main() {
    int arr[3] = { 1,2,3 };
    int* p = arr;
    cout << &arr[0] << ' ' << &arr[1] << '\n'; //000000464A4FF9D8 000000464A4FF9DC
    cout << &(*p) << '\n'; //000000464A4FF9D8
}
  • 배열 arr의 각 원소(arr[0], arr[1] 등)는 lvalue로, 주소 연산자(&)를 사용해 주소값을 얻을 수 있다.
  • 포인터 역참조 *p 또한 lvalue로, 다시 주소 연산자(&)를 붙여 주소값을 얻을 수 있다.



함수 이름, 함수가 lvalue 참조를 반환

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;

int& first(int* a) { return a[0]; }

int main() {
    auto *pf = &first;

    int a[2] = { 7,8 };
    first(a) = 42;
    pf(a) = 100;

    cout << a[0] << '\n'; //100
    cout << &first(a) << ' ' << &a[0] << '\n'; //000000DA922FF688 000000DA922FF688
}
  • 함수 이름 first는 함수 타입의 lvalue로, 주소 연산자(&)를 사용해 주소값을 얻을 수 있다.
  • 함수 first()의 반환 타입이 lvalue 참조인 int&이므로, 함수 호출식 first(a)은 lvalue이며 그 자체에 값을 할당할 수 있다.
  • 함수 포인터 pf를 통해 호출해도 동일하게 lvalue 참조를 반환하므로 pf(a) = 100; 또한 유효하다.
  • 함수 first()의 호출식이 lvalue이므로 주소 연산자(&)를 사용할 수 있다. &first(a)a[0]의 주소와 동일하다.

중제목중제목

1
코드코드
  • 설명설명


• rvalue •

1

• xvalue •

1

This post is licensed under CC BY 4.0 by the author.

[Pancht] 요트 다이스 AI 플레이 통계 및 분석

-