Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do c++ standard not guarantee pointer-interconvertibility between an array of objects and its first element? [duplicate]

The working draft of the standard N4659 says:

[basic.compound]
If two objects are pointer-interconvertible, then they have the same address

and then notes that

An array object and its first element are not pointer-interconvertible, even though they have the same address

What is the rationale for making an array object and its first element non-pointer-interconvertible? More generally, what is the rationale for distinguishing the notion of pointer-interconvertibility from the notion of having the same address? Isn't there a contradiction in there somewhere?

It would appear that given this sequence of statements

int a[10];

void* p1 = static_cast<void*>(&a[0]);
void* p2 = static_cast<void*>(&a);

int* i1 = static_cast<int*>(p1);
int* i2 = static_cast<int*>(p2);

we have p1 == p2, however, i1 is well defined and using i2 would result in UB.

like image 562
n. 1.8e9-where's-my-share m. Avatar asked Dec 21 '17 11:12

n. 1.8e9-where's-my-share m.


4 Answers

There are apparently existing implementations that optimize based on this. Consider:

struct A {
    double x[4];
    int n;
};

void g(double* p);

int f() {
    A a { {}, 42 };
    g(&a.x[1]);
    return a.n; // optimized to return 42;
                // valid only if you can't validly obtain &a.n from &a.x[1]
}

Given p = &a.x[1];, g might attempt to obtain access to a.n by reinterpret_cast<A*>(reinterpret_cast<double(*)[4]>(p - 1))->n. If the inner cast successfully yielded a pointer to a.x, then the outer cast will yield a pointer to a, giving the class member access defined behavior and thus outlawing the optimization.

like image 119
T.C. Avatar answered Oct 19 '22 19:10

T.C.


More generally, what is the rationale for distinguishing the notion of pointer-interconvertibility from the notion of having the same address?

It is hard if not impossible to answer why certain decisions are made by the standard, but this is my take.

Logically, pointers points to objects, not addresses. Addresses are the value representations of pointers. The distinction is particularly important when reusing the space of an object containing const members

struct S {
    const int i;
};

S s = {42};
auto ps = &s;
new (ps) S{420};
foo(ps->i);  // UB, requires std::launder

That a pointer with the same value representation can be used as if it were the same pointer should be thought of as the special case instead of the other way round.

Practically, the standard tries to place as little restriction as possible on implementations. Pointer-interconvertibility is the condition that pointers may be reinterpret_cast and yield the correct result. Seeing as how reinterpret_cast is meant to be compiled into nothing, it also means the pointers share the same value representation. Since that places more restrictions on implementations, the condition won't be given without compelling reasons.

like image 30
Passer By Avatar answered Oct 19 '22 19:10

Passer By


Because the comittee wants to make clear that an array is a low level concept an not a first class object: you cannot return an array nor assign to it for example. Pointer-interconvertibility is meant to be a concept between objects of same level: only standard layout classes or unions.

The concept is seldom used in the whole draft: in [expr.static.cast] where it appears as a special case, in [class.mem] where a note says that for standard layout classes, pointers an object and its first subobject are interconvertible, in [class.union] where pointers to the union and its non static data members are also declared interconvertible and in [ptr.launder].

That last occurence separates 2 use cases: either pointers are interconvertible, or one element is an array. This is stated in a remark and not in a note like it is in [basic.compound], so it makes it more clear that pointer-interconvertibility willingly does not concern arrays.

like image 37
Serge Ballesta Avatar answered Oct 19 '22 19:10

Serge Ballesta


Having read this section of Standard closely, I have the understanding that two objects are pointer-interconvertible, as the name suggests, if

  1. They are “interconnected”, through their class definition (note that pointer interconvertible concept is defined for a class object and its first non-static data member).

  2. They point to the same address. But, because their types are different, we need to “convert” their pointers' types, using reinterpret_cast operator.

For an array object, mentioned in the question, the array and its first element have no interconnectivity in terms of class definition and also we don’t need to convert their pointer types to be able to work with them. They just point to the same address.

like image 33
F14 Avatar answered Oct 19 '22 17:10

F14