C++ Value Category

History

  • 196x - CPL - Christopher Strachey

    lvalue, rvalue

  • 1966 - BCPL

  • 1978 - (K&R) C - Dennis Ritchie

    lvalue, not lvalue

  • 1986 - early C++ - Bjarne Stroustrup

    lvalue, rvalue

C++

C++ 的 expression 會用兩種性質來做區分, 一個是 type, 另一個是 value category。

其中 C++ 的 Value 又會依照兩種性質來區分, 一個是 identity, 另一個是可不可以被 move。

  • has identity: i.e. an address, a pointer, the user can determine whether two copies are identical, etc.

  • can be moved from: i.e. we are allowed to leave to source of a 「copy」 in some indeterminate, but valid state

Identity

Move

lvalue

O

X

iM

prvalue

X

O

Im

xvalue

O

O

im

not used

X

X

IM

glvalue

O

X/O

i

lvalue or xvalue

rvalue

X/O

O

m

prvalue or xvalue

lvalue   xvalue    prvalue

   \        /\        /
iM  \      /  \      / Im
     \    / im \    /
      \  /      \  /
       \/ i      \/ m

     glvalue   rvalue
   std::move
   +---------+
   |         |
   |         v
 lvalue   xvalue    prvalue

   \        /\        /
iM  \      /  \      / Im
     \    / im \    /
      \  /      \  /
       \/ i      \/ m

     glvalue   rvalue
      std::move
      +---------+
      |         |
      |         v
+-> lvalue   xvalue    prvalue
|
|     \        /\        /
|  iM  \      /  \      / Im
|       \    / im \    /
|        \  /      \  /
|         \/ i      \/ m
|
|       glvalue   rvalue
|
|                   |
+-------------------+
  rvalue reference
  • prvalues are the implicit moves

  • xvalues are the explicit moves

Examples

std::move : lvalue -> xvalue

#include <utility>

int main() {
    int x = 42;
    std::move(x) = 5566;
    return 0;
}
$ g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:5:18: error: using xvalue (rvalue reference) as lvalue
     std::move(x) = 5566;

rvalue reference : prvalue -> lvalue

#include <utility>

int main() {
    int&& x = 42;
    int&& y = x;
    return 0;
}
$ g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:5:15: error: cannot bind ‘int’ lvalue to ‘int&&’
     int&& y = x;
               ^
test.cpp:5:11: warning: unused variable ‘y’ [-Wunused-variable]
     int&& y = x;
           ^

rvalue reference : xvalue -> lvalue

#include <utility>

int main() {
    int x = 42;
    int&& y = std::move(x);
    int&& z = y;
    return 0;
}
$ g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:6:15: error: cannot bind ‘int’ lvalue to ‘int&&’
     int&& z = y;
               ^
test.cpp:6:11: warning: unused variable ‘z’ [-Wunused-variable]
     int&& z = y;
           ^