2

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?

Géry Ogam
  • 6,336
  • 4
  • 38
  • 67
  • 1
    They have names and you can get their addresses, right? You may consider them as const objects. – 273K Jul 12 '23 at 23:53
  • 5
    An lvalue is something that is not a temporary. A function is not a temporary so it is an lvalue. Assignment doesn't work with arrays either and they are lvalues as well. Also, you can assign to temporary class objects. Basically the adage that if you can assign to it, it is an lvalue doesn't hold except for fundamental types. – NathanOliver Jul 12 '23 at 23:56
  • "*Why are expressions that denote functions classified as L-value expressions?*" Why *shouldn't* they be? – Nicol Bolas Jul 13 '23 at 00:09
  • @NicolBolas Because functions names cannot be assigned to, so they don’t denote locations (L-values). – Géry Ogam Jul 13 '23 at 05:19
  • @NathanOliver-IsonStrike Thank you, but the definition that you provide is different from the original definition in denotational semantics. I have updated the post to clarify. – Géry Ogam Jul 13 '23 at 05:21
  • 1
    You've posted the C++ definition of an lvalue. It isn't "can be assigned to", which is a *different* definition that denotational semantics uses – Caleth Jul 13 '23 at 07:25
  • Or, to think of it differently, functions are bound to locations *exactly once*, at their definition, and can't be re-assigned – Caleth Jul 13 '23 at 07:28
  • @NathanOliver-IsonStrike My mistake was to derive *not assignable implies not L-value* from Strachey’s *assignable implies L-value*. – Géry Ogam Jul 13 '23 at 17:25
  • @Caleth Actually Strachey never stated that *assignable is equivalent to L-value*, he only stated that *assignable implies L-value*, but I incorrectly assumed he did so I derived by contraposition of *L-value implies assignable* that *not assignable implies not L-value*. So Strachey’s definition and C++ definition are consistent. – Géry Ogam Jul 13 '23 at 17:34

1 Answers1

3

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

  • can be moved from
  • has identity

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:

  • "abc" is an lvalue, but it cannot be assigned
  • x is also a non-assignable lvalue, if it is declared as const int x

The actual relation between lvalues and assignability is:

  • for something to be assignable, it must be an lvalue (operator overloads can soften this rule)
  • not every lvalue is assignable

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?

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
  • It's true that "things with names are lvalues" (_e.g._, even given `X &&x=…;`, `x` is an lvalue), but it isn't true that having an identity requires a name: `*new int` is an lvalue, but it doesn't have a name. The fundamental point is that an lvalue **is** an identity (rather than a newly constructed value), and an expression that names function is definitely identifying something rather than creating it. – Davis Herring Jul 13 '23 at 08:19
  • @DavisHerring I've rephrased the answer so that it doesn't imply that a name is required. Note that the answer also includes the example `"abc"`, which is an lvalue that has no name, so it should be obvious to the reader that an identifier isn't absolutely required. I don't think it's the right intuition to say *"an lvalue is an identity"*, because it makes little sense. The value category is a property of an expression, so it makes no sense to say "a property is an identity". However, it is true that the mere appearance of lvalues in an expression doesn't require *value computation*. – Jan Schultke Jul 13 '23 at 08:30
  • Thank you very much, I think you nailed it. As you said, Strachey only stated that *assignable implies L-value* but I incorrectly derived from it that *not assignable implies not L-value*. – Géry Ogam Jul 13 '23 at 17:20