I don't know if it has ever been true that all lvalues can be put on the left-hand side of an assignment but it's certainly not the case in C++.
I think a more accurate way to think about it (without drowning in standardese) is that rvalues refer to things that are about to get destroyed (e.g. temporaries) or that are no longer needed (things that you've called std::move on) while lvalues are more long-lived (it will still be valid to use them afterwards in the same scope).
Your function abs will not get destroyed, only objects get destroyed, so I would say it's "long-lived". You cannot assign to it but that's just because the assignment operator is not defined for functions.
String literals are arrays. Those arrays stays alive for the whole program. You are not allowed to assign to them because arrays cannot be assigned to. Even if you could assign to arrays you wouldn't be able to assign to string literals because those arrays are const.
Note that class objects can often be assigned to even if they're rvalues.
std::string() = ""; // useless but valid
The reason for this is that the assignment operator is technically just a member function, and you are allowed to call member functions on rvalues, even non-const ones.
It's totally meaningless. I just mentioned it to show that lvalues and rvalues have very little to do with which side of an assignment they're allowed.
But do note that for non-class types this sort of code is not allowed.
Functions can't be moved from. And since functions have unique addresses, I think they are considered to have "identity" too. So expressions like abs should be lvalues according to the table.
Back in 2020 we talked about that expressions with function type can be used to initialize rvalue references: https://cplusplus.com/forum/general/273175/#msg1178129
At that time I speculated that the exception to the rule existed to ease generic programming, and my opinion hasn't changed. The distinction between rvalue and lvalue doesn't seem to be important for functions.
Almost probably - and going down a rabbit hole! :) :)
In my mind (and the language lawyers will probably squawk), a function is an lvalue if it returns a ref - otherwise if it returns a value it's an rvalue.
A glvalue is an expression whose evaluation determines the identity of an object or function.
The name of a function (when used as an expresson) is a glvalue
An xvalue is a glvalue that denotes an object whose resources can be reused (usually because it is near the end of its lifetime).
The name of a function is not an xvalue
An lvalue is a glvalue that is not an xvalue
Ergo, The name of a function (when used as an expresson) is an lvalue
Note:
Historically, lvalues and rvalues were so-called because they could appear on the left- and right-hand side of an assignment (although this is no longer generally true); glvalues are “generalized” lvalues, prvalues are “pure” rvalues, and xvalues are “eXpiring” lvalues. Despite their names, these terms classify expressions, not values.
In my mind (and the language lawyers will probably squawk), a function is an lvalue if it returns a ref - otherwise if it returns a value it's an rvalue.
I think this is mostly correct except that a function call that returns an rvalue reference is actually an rvalue (an xvalue to be precise).
But now we're talking about function calls.
Juan's original question was about using the name of the function as an expression without calling it.