Lvalues ​​that don't designate objects in C ++ 14

I am using N3936 as a link here (please correct this question if any C ++ 14 text is different).

In 3.10, Lvalues ​​and rvalues ​​have:

Each expression refers to one of the fundamental classifications in this taxonomy: lvalue, xvalue, or prvalue.

However, the definition of lvalue states:

The value lvalue [...] denotes a function or object.

In the 4.1 Lvalue-to-r transformation, the text appears:

[...] In all other cases, the result of the conversion is determined in accordance with the following rules: [...] Otherwise, the value contained in the object indicated by glvalue is the result of prvalue.

My question is: what happens in the code where lvalue does not denote an object? There are two canonical examples:

Example 1:

int *p = nullptr; *p; int &q = *p; int a = *p; 

Example 2:

 int arr[4]; int *p = arr + 4; *p; int &q = *p; std::sort(arr, &q); 

Which lines (if any) are poorly formed and / or cause undefined behavior?

Referring to example 1: *p value of l? In my first quote it should be. However, my second quote excludes it, since *p does not denote an object. (This, of course, is not the value of x or prvalue).

But if you are interpreting my second quote, meaning *p is actually an lvalue, then it is not covered by the lvalue-to-rvalue conversion rules at all. You can accept the catch-all rule that "all that is not defined by the standard is undefined behavior", but then you must enable the existence of null references if the lvalue-to-rvalue conversion is not performed.

History: This issue was raised in DR 232 . In C ++ 11, a resolution from DR232 actually appeared. Quoting from N3337 Lvalue-to-rvalue conversion:

If the object to which the glvalue belongs is not an object of type T and is not an object of a type derived from T, or if the object is not initialized, the program that requires this conversion has undefined behavior.

which, apparently, allows for the existence of null references, this only eliminates the problem of performing the lvalue-to-rvalue conversion on one. Also discussed in this SO thread.

Resolution from DR232 no longer appears in the N3797 or N3936.

+6
source share
2 answers

It is not possible to create a reference to zero or a reference to a non-contact element of an array, since section 8.3.2 says (reading from project n3936) that

The link must be initialized to reference a valid object or function.

However, it is unclear that the formation of an expression with the value of the lvalue category represents “link initialization”. On the contrary, in fact, temporary objects are objects, and links are not objects, so you cannot say that *(a+n) initializes a temporary object of a reference type.

+2
source

I think the answer to this question, although probably not the answer you really need, is that it is incomplete or poorly specified, so we can’t say whether the examples you cited are poorly formed or cause undefined by current draft standard.

We can see this by looking at DR 232 and DR 453 .

DR 232 reports that there are standard conflicts regarding whether to translate a null pointer to undefined behavior:

At least a few places in the IS state that direct via null pointer produce undefined behavior: 1.9 [intro.execution] in clause 4 gives "dereferencing of a null pointer" as an example undefined and 8.3.2 [dcl.ref], clause 4 (in a note) uses this supposedly undefined behavior as an excuse for the non-existence of “null references”.

However, clause 5.3.1 [expr.unary.op], which describes the unary Operator "*", does not say that the behavior is undefined if the operand is a null pointer, as one would expect. In addition, at least one pass gives the dereference of the correct behavior of the null pointer: 5.2.8 [expr.typeid], clause 2, says

and introduces the concept of an empty lvalue, which is the result of an indication on a null pointer or one after the end of the array:

If there are any. If the pointer has a null pointer value (4.10 [conv.ptr]) or points one after the last element of an array object (5.7 [expr.add]), the result is an empty lvalue and does not refer to any object or function.

and suggests that this type of lvaue-rval conversion be undefined.

and DR 453 tell us that we don’t know what a real object is:

What is a "valid" object? In particular, the expression "real object" seems to exclude uninitialized objects, but the answer to Core Issue 363 clearly says that it is not an intention.

and assumes that linking a reference to an empty value is undefined behavior.

If the value of l to which the link is directly attached does not indicate an existing object or function of the corresponding type (8.5.3 [dcl.init.ref]), as well as a memory area of ​​suitable size and alignment, contain an object of the reference type (1.8 [intro.object] , 3.8 [basic.life], 3.9 [basic.types]), undefined behavior.

and includes the following examples in the sentence:

 int& f(int&); int& g(); extern int& ir3; int* ip = 0; int& ir1 = *ip; // undefined behavior: null pointer int& ir2 = f(ir3); // undefined behavior: ir3 not yet initialized int& ir3 = g(); int& ir4 = f(ir4); // ill-formed: ir4 used in its own initializer 

So, if we want to limit ourselves to actions only with intent, I feel that DR 232 and DR 453 provide the information we need to say that the intention is that null pointer conversion lvalue-rvalue is undefined behavior and reference to null pointer or undefined The value is also undefined.

Now, although it took some time to resolve both of these resolutions, they are both active with relatively recent updates, and the committee does not seem to agree with the basic premise that the reported defects are actual defects. Therefore, not knowing these two elements, this implies that it is impossible to answer your question using the current project standards.

+1
source

Source: https://habr.com/ru/post/976537/


All Articles