What is a concise, rigorous definition of the difference between an rvalue expression and an lvalue expression, that covers all possible cases? - c++

https://stackoverflow.com/a/3601661/368896 and others provide a definition of lvalue and rvalue (referencing the standard), as well as xvalue, glvalue, and prvalue.
However, the definition of rvalue refers to the definition of xvalue, and the definition of xvalue refers to "the result of certain kinds of expressions involving rvalue references".
Therefore, to understand the technical definition of rvalue, one must determine which kinds of "expressions involving rvalue references" are referred to in the above definition. I have taken the time to attempt to follow the standard (and different postings) to track through this definition. I would like a definition that does not require tracking through other definitions (except for more obvious, lower-level definitions).
Is this a wild goose chase? Is it possible to provide a concise, but rigorous, definition of the difference between an rvalue expression and an lvalue expression, that covers all possible cases, and that does not make reference to "the result of certain kinds of expressions involving rvalue references"? If so, what is such a definition?

What constitutes an rvalue is defined by a set of ad-hoc rules, so I don't think "concise" is going to be possible.
xvalues are fairly easy: they're "stuff that returns a T&&." This includes functions returning that, explicit casts to T&&, accessing a class data member via a T&& (whether directly or via pointer-to-data-member).
The problem is prvalues, which are all over the place. You can get a general sense of what they are, but the specifics involves a lot of ad-hoc rules. Like the fact that return valueArgument; can consider valueArgument to be a prvalue, even though it's an lvalue everywhere else.
Basically, the standards committee went through the standard and looked for places where they wanted to implicitly move, and then said, "You're a prvalue."
So a "concise but rigorous" definition is not going to be likely.

Related

Is an address sometimes referred to as an 'l-value'

Studying for an exam in my programming languages class. Came across this excerpt in the textbook.
"The address of a variable is sometimes called its l-value, because the address is what is required when the name of a variable appears in the left side of an assignment."
I've been studying C++ on my own time for some years outside of college, and at the very least the topic of value categories is complex enough that I've had to review it many times to get it somewhat well down in my head, and still have to pull up the cppreference page to review the newer categories. At the very least this seems like a massive over-simplification. Is this simply false? Or is this use of l-value just something I've never come across?
EDIT:
The textbook is on Programming languages in general, not specifically C or C++. Apologies for the confusion.
The meaning of "lvalue" or "l-value" has a long and complex history. In some older definitions of the term, that sentence is correct -- but it's not consistent with the way the C++ standard defines it. If your textbook was discussing C++, that passage is questionable. If it was discussing programming in general, it's probably OK.
In C++ (and also in C), an lvalue is not a value. It is a kind of expression, i.e., a construct that can appear in C++ source code. It is the expression itself, not the result of evaluating it.
The definition in the C++11 standard is:
An lvalue (so called, historically, because lvalues could appear on
the left-hand side of an assignment expression) designates a function
or an object.
That definition is in the context of categorizing expressions.
When the terms "lvalue" and "rvalue" were first invented, they referred to things that can appear on the left ('l') or right ('r') side of an assignment. In the original meaning an expression like x could be "evaluated for its lvalue", meaning that it identifies the object named x, or "evaluated for its rvalue", meaning that it retrieves the value currently stored in x. The C and C++ standards have changed the meanings of the terms so that an "lvalue" is an expression, not the result of evaluating it.
(Having said that, the section of the C++11 standard that discusses all this can be a little vague, and there is wording that could suggest that an lvalue might be the result of evaluating an expression.)

Why is `is_­destructible` defined using `declval<U&>().~U()` and not `declval<U>().~U()`?

By the definition of is_destructible (http://eel.is/c++draft/meta.unary.prop#lib:is_destructible), is_­destructible_v<T> is true when:
Either T is a reference type, or T is a complete object type for which the expression declval<U&>().~U() is well-formed when treated as an unevaluated operand, where U is remove_­all_­extents_­t<T>.
Why does it use declval<U&>().~U() and not declval<U>().~U()?
The wording with declval was added in https://cplusplus.github.io/LWG/issue2049 to solve the problem the definition had with abstract types. Maybe the author was thinking that declval<U> has return type U so it won't work for abstract types?
So I asked Daniel Krügler via email and he allowed me to publish his answer:
Good question - albeit the answer is rather trivial and doesn't reveal
any language secret: I was aware that std::declval<T>() would return
an rvalue reference (and thus an rvalue) in the discussed context, but
in my mental imagination I wanted to express the picture of
translating p->~T(), which again according to the language corresponds
to (*p).~T() ([expr.ref]), so the logical consequence was to change
the std::declval() call to generate an lvalue of T where the
destructor was applied to.
I'm pretty sure that I didn't believe that declval() was returning the
T directly, this helper function was too deeply burned into my mind ;-)

Definition of "convertible to" used in ISO C++ library clauses

The response of LWG 484 shows "convertible" is "well-defined in the core". Which clauses does it refer to?
I have this question because currently the requirements of is_convertible are carefully defined in [meta.rel]/5, which are not in the core. Although it has clearly noted the technical benefits, the coexistence with other styles seems confusing in the overall library clauses. So there are more questions about the general use.
Do the meaning of requirements implied by is_convertible and the "convertible to" (without the code font) wording in the library clauses exactly the same?
If true, why both are used?
If not, is there any guideline or rationale to differentiate these subtle use cases?
Convertible to refers to something that can be a result of a standard conversion sequence defined in [conv] and since is_convertible is defined in terms of what conversion the core language allows during the copy-initialization of a return value, I don't see any difference.
Edit: Implicit conversion sequence might be another useful term not mentioned in the question. It is defined in [over.best.ics]
The "'convertible' is well-defined in core" statement was added at some point between 2004 and 2009, though that page doesn't say exactly when. So I looked at the C++03 and C++11 standards, and it appears that neither actually has a definition of the term "convertible". However, I think we can infer that "convertible" is generally intended to mean "implicitly convertible", and the meaning of "implicitly convertible" is given by [conv] ([conv.general] in newer editions of the standard).
For example C++11 [conv]/3 says:
An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed, for some invented temporary variable t (8.5). ...
However, it should be noted that the definition of "implicitly converted" uses an expression, not a type, as the source, whereas "convertible to" in the library sometimes has a type as the source. In that case, "T is convertible to U" should be interpreted as "all expressions of type T can be implicitly converted to type U".
There are other plausible meanings of "convertible", for example:
The first sentence of [expr.static.cast] says "The result of the expression static_cast<T>(v) is the result of converting the expression v to type T". This is more powerful than an implicit conversion. It can, for example, convert void* to int*, or from an enum type to an integral type.
The sections of the C++ standard that specify the behaviour of (T) cast-expression and T ( expression-list(opt) ) are titled "Explicit type conversion (cast notation)" and "Explicit type conversion (functional notation)". These conversions are much more powerful than implicit conversions; they can call explicit constructors and explicit conversion operators, and even perform static_casts, reinterpret_casts, and const_casts (and even bypass access control in some cases).
But if we look at how the term "convertible" is actually used in the standard, it seems that it must be intended to mean "implicitly convertible". For example, C++11 [allocator.uses.trait]/1 says:
Remark: automatically detects whether T has a nested allocator_type that is convertible from Alloc. Meets the BinaryTypeTrait requirements (20.9.1). The implementation shall provide a definition
that is derived from true_type if a type T::allocator_type exists and is_convertible<Alloc, T::allocator_type>::value != false, otherwise it shall be derived from false_type. [...]
Evidently the intent here was that "convertible" means the same thing as what is_convertible tests for, that is, implicit convertibility (with a caveat that will be discussed below).
Another example is that in C++11 [container.requirements.general], Table 96, it is stated that for a container X, the type X::iterator shall be "convertible to X::const_iterator". The general understanding of this is that it means implicitly convertible, i.e., this is well-formed:
void foo(std::vector<int>& v) {
std::vector<int>::const_iterator it = v.begin();
}
Imagine if you had to use a cast, or even just direct-initialization notation, to perform this conversion. Could LWG have meant to use the word "convertible" to with a meaning that would make such an implementation conforming? It seems very doubtful.
Wait, does "convertible to" have the same meaning as what the standard type trait std::is_convertible tests for? Assuming that "convertible to" means "implicitly convertible to" with the caveat above, strictly speaking, no, because there is the edge case where std::is_convertible<void, void>::value is true but void is not implicitly convertible to itself (since the definition of a void variable is ill-formed). I think this is the only edge case (also including the other 15 variations involving cv-qualified void), although I'm not 100% sure about that.
However, this doesn't explain why std::is_convertible is sometimes used in the library specification. Actually, it is only used once in C++11 (other than in its own definition), namely in [allocator.uses.trait]/1, and there, it doesn't seem that there is any intent for it to mean anything other than "implicitly convertible", since it is irrelevant what happens when Alloc is void (as it is mentioned elsewhere that non-class types are not permitted as allocator types). The fact that std::is_convertible is even mentioned here seems like it's just a mild case of an implementation detail leaking into the specification. This is probably also the case for most other mentions that appear in newer editions of the standard.
C++20 introduced a concept called std::convertible_to, where std::convertible_to<From, To> is only modelled if
From is implicitly convertible to To, and
static_cast<To>(e) is well-formed, where e has type std::add_rvalue_reference_t<From> (i.e., From is also explicitly convertible to To via static_cast), and
some other requirements hold, which I won't get into here.
It's reasonable to ask whether some uses of "convertible to" actually mean std::convertible_to, given the similar names, and other considerations. In particular, the one mentioned by LWG 484 itself. C++20 [input.iterators] Table 85 states that if X meets the Cpp17InputIterator requirements for the value type T, then *a must be "convertible to T", where a has type X or const X.
This is almost certainly another case where "convertible to", as used in the library specification, implies "implicitly convertible to", but I think that it also should imply "explicitly convertible to", where the implicit and explicit conversions give the same results. We can construct pathological iterator types for which they don't. See this Godbolt link for an example.
I think both users and standard library implementers should feel free to assume that if a function accepts input iterators, it can assume that explicit conversions via static_cast or direct-initialization notation from *a to T can be used with the same result as implicit conversions. (Such explicit conversions may, for example, be used when calling function templates, if we want the deduced type of the function template specialization's argument to be T& or const T& and not some other U& or const U& where U is the actual type of *a.) In other words, I think "convertible to" in the input iterator requirements really means something like std::convertible_to, not std::is_convertible_v. I would be surprised if standard library implementations truly only relied on implicit convertibility in this case, and not both implicit and explicit convertibility (with the two being equivalent). (They can implement the library in an extremely careful way that only uses implicit conversions in cases where one would normally use an explicit conversion, but I'm not sure if they actually do. And even if they are indeed that clever, it is not reasonable to expect the same from anyone who is not a standard library implementer.)
But that's an issue for LWG to resolve. Notice that LWG 484 was reopened in 2009 and remains open to this day. It's probably a lot of work to go through all the uses of "convertible to" and "convertible from" and determine which ones should mean "implicitly convertible to" and "implicitly convertible from" and which ones should mean std::convertible_to (as well as if any uses of "implicitly convertible to" need to be changed to std::convertible_to).

How are unqualified-ids other than identifier categorized in terms of lvalue and rvalue?

It seems that the standard does not explicitly talk about the expression categories of some unqualified-ids. On the other hand, an identifier, which is one of unqualified-ids, is categorized as follows:
The result is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise.
Then, what about other unqualified-ids—operator-function-id, conversion-function-id, literal-operator-id, ~class-name, ~decltype-specifier, template-id?
This has been acknowledged as Core Working Group issue 536 which has been submitted almost 10 years ago. So probably it hasn't been deemed important enough to be fixed.
More importantly, some kinds of id-expressions are not described by
5.1.1 [expr.prim.general]. The structure of this section is that the result, type, and lvalue-ness are specified for each of the cases it
covers [...]
This treatment leaves unspecified all the non-identifier
unqualified-ids (operator-function-id, conversion-function-id, and
template-id) [...]
-- CWG 536
From the behaviour of various compilers, we can assume the intention is that all of those expressions follow the rule for identifiers:
The type of the expression is the type of the identifier. The result is the entity denoted by the identifier. The result is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise.
-- post-N4296 [expr.prim.general]p13
By the way: all of those expressions but template-ids can only refer to functions. Template-ids can refer to variables as well. Both template-ids and ordinary identifiers can refer to classes and enums, which confused me slightly (can there be expressions such as int? are they prvalues?)

What am I allowed to do with a static, constexpr, in-class initialized data member?

This is probably a bit of an unusual question, in that it asks for a fuller explanation of a short answer given to another question and of some aspects of the C++11 Standard related to it.
For ease of reference, I shall sum up the referenced question here. The OP defines a class:
struct Account
{
static constexpr int period = 30;
void foo(const int &) { }
void bar() { foo(period); } //no error?
};
and is wondering why he gets no error about his usage of an in-class initialized static data member (a book mentioned this to be illegal). Johannes Schaub's answer states, that:
This violates the One Definition Rule;
No diagnostics is required.
As much as I rely the source and validity of this answer, I honestly dislike it because I personally find it too cryptic, so I tried to work out a more meaningful answer myself, with only partial success. Relevant seems to be § 9.4.2/4:
"There shall be exactly one definition of a static data member that is odr-used (3.2) in a program; no diagnostic is required" [Emphases are mine]
Which gets me a bit closer to the point. And this is how § 3.2/2 defines an odr-used variable:
"A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied" [Emphases are mine]
In the OP's question, variable period clearly satisfies the requirements for appearing in a constant expression, being a constexpr variable. So the reason must be certainly found in the second condition: "and the lvalue-to-rvalue conversion (4.1) is immediately applied".
This is where I have troubles interpreting the Standard. What does this second condition actually mean? What are the situations it covers? Does it mean that a static constexpr variable is not odr-used (and therefore can be in-class initialized) if it is returned from a function?
More generally: What are you allowed to do with a static constexpr variable so that you can in-class initialize it?
Does it mean that a static constexpr variable is not odr-used (and
therefore can be in-class initialized) if it is returned from a
function?
Yes.
Essentially, as long as you treat it as a value, rather than an object, then it is not odr-used. Consider that if you pasted in the value, the code would function identically- this is when it is treated as an rvalue. But there are some scenarios where it would not.
There are only a few scenarios where lvalue-to-rvalue conversion is not performed on primitives, and that's reference binding, &obj, and probably a couple others, but it's very few. Remember that, if the compiler gives you a const int& referring to period, then you must be able to take it's address, and furthermore, this address must be the same for each TU. That means, in C++'s horrendous TU system, that there must be one explicit definition.
If it is not odr-used, the compiler can make a copy in each TU, or substitute the value, or whatever it wants, and you can't observe the difference.
You missed a part of the premise. The class definition give above is completely valid, if you also define Account::period somewhere (but without providing an initializer). See the last sentance of 9.4.2/3:
The member shall still be defined in a namespace scope if it is odr-used
(3.2) in the program and the namespace scope definition shall not
contain an initializer.
This entire discussion only applies to static constexpr data members, not to namespace-scope static variables. When static data members are constexpr (rather than simply const) you have to initialize them in the class definition. So your question should really be: What are you allowed to do with a static constexpr data member so that you don't have to provide a definition for it at namespace scope?
From the preceding, the answer is that the member must not be odr-used. And you already found relevant parts of the definition of odr-used. So you can use the member in a context where it is not potentially-evaluated - as an unevaluated operand (for example of sizeofor decltype) or a subexpression thereof. And you can use it in an expression where the lvalue-to-rvalue conversion is immediately applied.
So now we are down to What uses of a variable cause an immediate lvalue to rvalue conversion?
Part of that answer is in §5/8:
Whenever a glvalue expression appears as an operand of an operator
that expects a prvalue for that operand, the lvalue-to-rvalue (4.1),
array-to-pointer (4.2), or function-to-pointer (4.3) standard
conversions are applied to convert the expression to a prvalue.
For arithmetic types that essentially applies to all operators that apply standard arithmetic conversions. So you can use the member in various arithmetic and logical operations without needing a definition.
I can't enumerate all things, you can or can't do here, because the requirements that something be a [g]lvalue or [p]rvalue are spread across the standard. The rule of thumb is: if only the value of the variable is used, a lvalue to rvalue conversion is applied; if the variable is used as an object, it is used as lvalue.
You can't use it in contexts where an lvalue is explicitly required, for example as argument to the address-of operator or mutating operators). Direct binding of lvalue references (without conversion) is such a context.
Some more examples (without detailed standardese analysis):
You can pass it to functions, unless the function parameter is a reference to which the variable can be directly bound.
For returning it from a function: if implicit conversion to the return type involves a user-defined conversion function, the rules for passing to a function apply. Otherwise you can return it, unless the function returns an lvalue (a reference) referring directly to the variable.
The key rule for odr-used variables, the "One Definition Rule" is in 3.2/3:
Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program; no diagnostic
required.
The "no diagnostic required" part means that programs violating this rule cause undefined behavior, which may range from failing to compile, compiling and failing in surprising ways to compiling and acting as if everything was OK. And your compiler need not warn you about the problem.
The reason, as others have already indicated, is that many of these violations would only be detected by a linker. But optimizations may have removed references to objects, so that no cause for linkage failure remains or else linking may sometimes occur only at runtime or be defined to pick an arbitrary instance from multiple definitions of a name.