Skip to content

Latest commit

 

History

History
190 lines (132 loc) · 6.93 KB

File metadata and controls

190 lines (132 loc) · 6.93 KB

Value categories

Move semantic is achieved by adding a new category type to the type system, it is not a library feature and cannot be reproduced in C++03.

The value categories in C++ changed from C during it’s first standardisation, then changed in C++11 to enable move semantic, and again in C++17 to enable further optimisation (for example for returning non-movable and non-copyable types from functions).

So, depending on the context/language revision, categorie values have different definitions.

Category values in K&R C

int i = 42;    // i is lvalue, 42 is rvalue
42 = i;        // invalid, 42 is not an rvalue
int* p = &i;   // p is lvalue, i is lvalue, and &i is lvalue
int* q = &42;  // invalid, 42 is not an rvalue

i is an lvalue (on the left-hand side-of an assignment) 42 is an rvalue (only on the right-hand-side of an assignment)

Variables are lvalue, literals (except string literals) and functions are rvalues.

Category values in K&R C

String literals are rvalue, otherwise it would not be possible to take the address to the first element. Function are lvalues, but it is still possible to take theyr address.

char (*string1)[] = &"Hello World";  // type char(*)[12]
char* string2 = "Hello World";       // type char*
char* string3 = &"Hello World";      // error, as char* is not compatible with char(*)[12]
"Hello World"[0] = 'a';              // valid, but UB


void fun(void);
void (*f1)(void) = fun;
void (*f2)(void) = &fun;
fun = ...; // invalid, as fun is rvalue

Category values in C

Same as K&R C, but there is just lvalue. Also some rules have been added to support const.

string literals are constant lvalues (but because of backwards compatibility, they still are still implicitely convertible non-constant lvalues). Function and other literals count as others.

Category values in C

const applies to lvalue expressions, they are not modifiable. For example, they are not assignable.

A pointer to an non-const type can be implicitly converted to a pointer to a const-qualified version of the same or compatible type. The reverse conversion can be performed only with an explicit cast.

const int c = 0;    // c is an lvalue
c = 42;             // error, c is const-qualified and not assignable
const int* p = &c;  // c is an lvalue
int* p = &c;        // error, const int* is not convertible to int*
const int* q = &42; // invalid, 42 is an rvalue
"hello"[0] = 'a'; // valid, but UB
const char (*v)[12] = &"Hello World";
v[0] = 'a'; // invalid
const char* vv = "Hello World";
vv[0] = 'a'; // invalid

Category values in C++ <11

C++98 and C++03 followed C and restored the name "rvalue" to non-lvalue expressions.

Some expression, like functions, are lvalues, and string literals are const withtout exceptions.

References can bind to lvalues, but only references to const can bind to rvalues. In the second case, the lifetime of the object is extended until the scope of the reference ends.

General rule: if it is possible to take an address/it has a name, then it’s a lvalue.

Category values in C++ <11

int& c = 42;       // invalid, 42 is rvalue, cannot bind reference to it
const int& c = 42; // c is lvalue, 42 is rvalue, but references to const can bind to rvalues

int j = 42;  // j is lvalue, 42 is rvalue
int& c = j;  // c,j are both lvalue

void foo(int&);
foo(42);  // error, cannot bind lvalue to rvalue

int baz();       // returns a rvalue
bar(const int&); // takes const reference
bar(baz());      // const reference can bind to rvalue

void f( const std::string& s );
f( "foobar" ); // const reference can bind to rvalue (array is converted to std::string)

"hello"[0] = 'a'; // error, "hello" is a const lvalue

Category values in C++ >=11 and <17

C++11 and C++14 introduced new values to enable move semantic. Value categories were redefined to characterize two independent properties of expressions:

  • has identity: it’s possible to determine if two expression refers to the same entity, for example by comparing addresses of the objects.

  • can be moved from: move constructor, or another function overload that implements move semantics can bind to the expression.

Category values in C++ >=11 and <17

Thus there are expression that

  • have identity and cannot be moved from (lvalue expressions)

  • have identity and can be moved from (xvalue (eXpiring Value) expressions)

  • do not have identity and can be moved from are (prvalue ("pure rvalue") expressions);

  • do not have identity and cannot be moved from (unused).

Category values in C++ >=11 and <17

The expressions that have identity are called "glvalue expressions" (glvalue stands for "generalized lvalue"). Both lvalues and xvalues are glvalue expressions. The expressions that can be moved from are called "rvalue expressions". Both prvalues and xvalues are rvalue expressions.

Category values in C++ >=11 and <17

value types

Category values in C++ >=11 and <17

Notice that both lvalue and rvalue where reused with similar meaning, and following relation holds in all C++ standards:

Every value is either an lvalue or an rvalue, never both.

Category values in C++ >=11 and <17

std::string s;

&s;                     // ok, lvalue (just like c++ <11 lvalue)
s = "a";                // ok, lvalue (just like c++ <11 lvalue)

&std::string(s);        // error, prvalue (just like c++ <11 rvalue)
std::string(s) = a;     // error, prvalue (just like c++ <11 rvalue)

&std::move(s);          // error, xvalue (similar to c++ <11 rvalue)
std::move(s) = "a";     // ok, xvalue (similar to c++ <11 lvalue)

Category values in C++ >=11 and <17

In practice:

  • Everything that has a name (variables, parameters, functions, …​), *this, and string literals are lvalue.

  • All literals (42, nullptr, -1f, enums, …​ strings exlcuded), this, lambda, return of constructor call and value returned from functions are prvalue.

  • returned value reference (by std::move) and cast to rvalue reference are xvalue.