c++functionlvaluedenotational-semantics

Why are functions names classified as L-value expressions?


According to Christopher Strachey’s paper The Varieties of Programming Language on denotational semantics, in any programming languages names can be bound to bindable values, which is represented by the function environment: Id -> D, where Id are names (a.k.a. identifiers) and D are denotations (a.k.a. bindable values). If the language has an assignment command, the assignable value denoted by a name can vary inside the lexical scope of the name through assignment, but the location holding the assignable value remains constant, so to keep the environment static, only locations are considered as bindable values (i.e. D includes L but not V), and the dynamic part that associates locations to assignable values is represented by the function state: L -> V, where L are locations (a.k.a. L-values) and V are stored values (a.k.a. R-values, contents, or assignable values).

In C++, functions names cannot be assigned to so they don’t denote locations (i.e. function values F are not included in V but in D, that is D = L + F + …):

void f();  // binding
void g();  // binding

int main() {
    f = g;  // assignment fails
    return 0;
}

Output of clang++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out:

main.cpp:5:7: error: non-object type 'void ()' is not assignable
    f = g;  // assignment fails
    ~ ^
1 error generated.

Yet according to the C++ standard, function names are classified as L-value expressions (bold emphasis mine):

  • A glvalue is an expression whose evaluation determines the identity of an object or function.
  • A prvalue is an expression whose evaluation initializes an object or computes the value of an operand of an operator, as specified by the context in which it appears, or an expression that has type cv void.
  • An xvalue is a glvalue that denotes an object whose resources can be reused (usually because it is near the end of its lifetime).
  • An lvalue is a glvalue that is not an xvalue.
  • An rvalue is a prvalue or an xvalue.

Why?


Solution

  • Value categories in C++ are characterized by two properties:

    It's not about whether something is assignable, although the value category determines whether the assignment operator can be used.

    Immovable Movable
    Anonymous prvalue
    Has Identity lvalue xvalue

    Note: there are two mixed categories: lvalues and xvalues are glvalues, and prvalues and xvalues are rvalues.

    Functions "have an identity", which means that there is a name or something else that denotes them. In this case, it's the name of the function. For example, the name main denotes the main function, so it has to be an lvalue or xvalue expression (i.e. a gvalue). Furthermore, function cannot be moved from, so it only makes sense for them to be lvalues, not xvalues.

    Conflating "can be assigned" in the C++ sense with this system is the wrong way to think about it. There are other non-assignable lvalue expressions, such as string literals, or names of const objects:

    The actual relation between lvalues and assignability is:

    This doesn't contradict what the author was saying because the author didn't say that every L-value must be mutable.


    See also: What are rvalues, lvalues, xvalues, glvalues, and prvalues?