-
[c++] Value Category ( lvalue, prvalue, xvalue ) , decltype함수Programming/c++ 2023. 10. 20. 16:16728x90
타입과 value category는 다른 개념입니다!
Value Category
c++에서는 5가지의 값 카테고리가 존재합니다.
- lvalue ( left value ) : 좌측값 - rvalue (right value ) : 우측값 - prvalue ( pure rvalue ) : 순수 우측값 - xvalue ( expiring value) : 소멸하는 값 - glvalue ( generalized lvalue) : 일반화된 좌측값
즉, 값 카테고리는 좌측값/우측값을 말합니다.
값 카테고리를 구분하기 위해 2가지 조건이 있습니다.
- 정체를 알 수 있는가?
: 해당 식이 다른 식과 같은지 다른지 구분할 수 있다.
일반적인 변수라면 주소값을 취해서 구분할 수 있고, 함수의 경우라면 이름만 확인하면 된다.
- 이동 시킬 수 있는가?
: 해당 식을 다른 곳으로 안전하게 이동할 수 있는지 여부.
해당 식을 받는 이동 생성자, 이동 대입 연산자 등을 사용할 수 있어야 한다.
이동 시킬 수 있다 이동 시킬 수 없다 정체를 알 수 있다 xvalue lvalue 정체를 알 수 없다 prvalue 쓸모 없음 정체를 알 수 있는 모든 식 : glvalue
이동 시킬 수 있는 모든 식 : rvalue
lvalue
이름을 가진 대부분의 객체들은 모두 lvalue입니다. 해당 객체의 주소값을 취할 수 있기 때문입니다.
1. 변수, 함수의 이름, 어떤 타입의 데이터 멤버 : std::endl, std::cin 등등 2. 좌측값 레퍼런스를 리턴하는 함수의 호출식 : std::cout << l, ++it 등등 3. 복합 대입 연산자 식 : a = b, a+= b, a *= b 4. 전위 증감 연산자 식 : ++a, --a 5. 멤버를 참조할 때 : a.m, p->m (m이 enum 값이거나, static아닌 멤버 함수인 경우 제외)
class A { int f(); // static 이 아닌 멤버 함수 static int g(); // static 인 멤버 함수 } A a; a.g; // <-- lvalue a.f; // <-- lvalue 아님 prvalue임
lvalue들은 주소값 연산자(&)를 통해 해당 식의 주소값을 알아 낼 수 있습니다.
또한, 좌측값 레퍼런스를 초기화하는데 사용할 수 있습니다.
prvalue
정체를 알 수 없기에 주소값을 취할 수 없습니다.
&a++, &42는 모두 오류입니다.
prvalue는 좌측에 올 수 없습니다.
prvalue는 우측값 레퍼런스와 상수 좌측값 레퍼런스를 초기화하는데 사용할 수 있습니다.
1. 문자열 리터럴을 제외한 모든 리터럴들 : 42, true, nullptr 등등 2. 레퍼런스가 아닌 것을 리턴하는 함수의 호출식 : str.substr(1, 2), str1 + str1 3. 후위 증감 연산자 식 : a++, a-- 4. 산술 연산자, 논리 연산자 : a + b, a && b, a < b 5. 주소값 연산자 식 : &a 6. 멤버 참조 : a.m, p->m ( m은 enum 값이거나 static 이 아닌 멤버 함수) 7. this 8. enum 값 9. 람다식 : []() {return 0;};
xvalue
좌측값처럼 정체가 있지만 이동도 시킬 수 있는 것
우측값 레퍼런스를 리턴하는 함수의 호출식 : std::move(x)
예제
int i = 40; auto && ri = i; // ri는 lvalue, lvalue인 i가 들어왔기 때문. int& ri auto && ri2 = 42; // ri2는 rvalue. int&& ri2 auto && ri3 = std::move(i); // ri3는 rvalue. int && ri3
decltype
식의 구체적인 타입 그대로 전달하는(추출하는) 키워드
- 식의 값 종류가 xvalue 라면 decltype 는 T&& 입니다. - 식의 값 종류가 lvalue 라면 decltype 는 T& 입니다. - 식의 값 종류가 prvalue 라면 decltype는 T 입니다.
컴파일 타임에 auto가 타입을 추론하기에 역부족일 때 주로 사용.
런타임이 아닌 컴파일 타임에 결정된다.
decltype : 변수가 선언된 타입 그대로 가져온다. const, & 까지
decltype((x)) : 괄호가 두개면 래퍼런스(&)를 추가로 붙여준다.
typedef decltype((x)) cx_typee; //&을 추가로 붙여서 int & typedef decltype((cx)) cx_with_parens_type; //&을 추가로 붙여서 const int & typedef decltype((crx)) crx_with_parens_type; //const int & //원래 &가 있는 상태면 그대로 & 유지함. typedef decltype((p->m_x)) m_x_with_parens_type; //&가 추가로 붙는데 그냥 int &가 되는게 아닌 const 까지 따라와서 const int &가 된다. //p는 const이기 때문에 간접참조로 멤버의 값을 바꾸면 안되기 때문에 & 레퍼런스가 되니 값을 바꾸면 안된다는 const까지 따라 온다.
참고 레퍼런스
728x90'Programming > c++' 카테고리의 다른 글
[c++] std::move (1) 2023.10.20 [C++] 공용체 Union (0) 2023.09.13 [c++] trim( ), 공백, 개행, 탭 제거 (whitespace 제거) (0) 2023.06.15 [c++] multiset (0) 2023.02.12 [c++] priority_queue (0) 2023.02.12