ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [c++] Value Category ( lvalue, prvalue, xvalue ) , decltype함수
    Programming/c++ 2023. 10. 20. 16:16
    728x90

    타입과 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까지 따라 온다.

     

     

     

    참고 레퍼런스

    https://modoocode.com/294

     

    https://ansohxxn.github.io/cpp/chapter19-8/#%EC%98%88%EC%A0%9C-2%EF%B8%8F%E2%83%A3-decltype%EC%9D%80-%EB%A6%AC%ED%84%B4-%ED%83%80%EC%9E%85%EC%97%90%EB%8F%84-%EC%82%AC%EC%9A%A9-%EA%B0%80%EB%8A%A5-%ED%95%98%EB%8B%A4

     

    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

    댓글

© 2022. code-space ALL RIGHTS RESERVED.