“이름이 있고, 주소를 잡을 수 있고, 계속 존재하는 객체”를 나타내는 표현식
&expr 해서 주소를 얻을 수 있는 애들이 대부분 lvalue
int x = 10;
x; // x → lvalue
int& r = x; // r → lvalue (참조도 결국 객체의 또 다른 이름)
struct S { int a; };
S s;
s; // s → lvalue
s.a; // s.a도 lvalue
x, s, s.a 전부 “실제 메모리 어딘가에 있는 객체”
나중에 다시 읽고/쓰고 싶을 때 재사용 가능한 애들 → lvalue
“계산 결과로 나오는 값 자체” / 주로 임시 객체, 리터럴 같은 것
예전 C++에서 말하던 “rvalue”에 제일 가까움
보통 새 객체를 만들거나, 값만 있다가 사라지는 애들
10; // int literal → prvalue
3.14; // double literal → prvalue
true; // bool literal → prvalue
int x = 10;
x + 1; // x + 1 → prvalue
std::string("abc"); // 임시 std::string 객체 → prvalue
S(); // 임시 S 객체 → prvalue
이 값들은 “메모리 어딘가에 이름 붙은 객체”랑 연결되어 있는 느낌이 아니라,
“계산 결과로 잠깐 존재하는 값”에 더 가깝다 → prvalue
xvalue (expiring value)는:
“곧 수명이 끝나서 자원을 빼앗아도 되는 객체 표현”
“진짜 객체를 가리키긴 하는데, 더 이상 이걸 원래 주인처럼 쓰지 않을 것” 이라는 의미를 가진 값
대표적으로 rvalue reference로 표현된 객체가 xvalue가 됨.
std::string make();
std::string s = "hello";
std::string&& r = std::move(s);
r; // r 자체는 lvalue (이름 있는 변수)
std::move(s); // 이 표현식 → xvalue
std::move(r); // 이것도 표현식 → xvalue
std::move(s) 는:
“s라는 실제 객체” (메모리 상에 존재)를 가리키면서
“얘는 이제 곧 자원 뺏어가도 된다”는 의미를 갖는 표현식 → xvalue
즉:
lvalue: “정상적으로 계속 쓸 객체”
xvalue: “아직 존재하긴 하는데, 이제 자원을 뺏어가도 되는 객체”
또 다른 예:
std::vector<int> v = {1,2,3};
v[0]; // lvalue
std::move(v)[0]; // xvalue (이 표현식이 가리키는 원소)
glvalue는:
즉, lvalue ∪ xvalue
다시 말해:
lvalue : 정상적으로 쓰일 객체
xvalue : 곧 만료(expire)될 객체
둘 다 “실제 객체”를 가리킨다는 점에서 공통됨 → 그걸 묶어서 glvalue라고 부름.
lvalue 예시: x, s, s.a, v[0]
xvalue 예시: std::move(s), std::move(v)[0]
→ 이걸 전부 glvalue라고 부를 수 있다.
rvalue는:
“주로 임시 객체, 이동 대상(move 대상)”
즉, prvalue ∪ xvalue
예전 C++에서 “rvalue”라고 부르던 녀석들이
C++11 이후에는 prvalue / xvalue로 나뉘고
그 둘을 묶어서 다시 rvalue라고 부르게 된 것.
10; // prvalue → rvalue
x + 1; // prvalue → rvalue
S(); // prvalue → rvalue
std::string("abc"); // prvalue → rvalue
std::move(s); // xvalue → rvalue
std::move(v)[0]; // xvalue → rvalue
정리하면:
prvalue : 값 자체, 새로 만들어지는 임시, 리터럴
xvalue : 실제 객체를 가리키지만 “만료(expiring)된” 상태
이 둘을 합쳐서 → rvalue
glvalue = lvalue ∪ xvalue
rvalue = prvalue ∪ xvalue
즉 xvalue는 glvalue이면서 rvalue에도 포함돼 있다.