Pointer interconvertibility vs having the same address
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.
Pointer-interconvertibility of derived and base class objects and the Standard's wording on it
The point is in definition of standard-layout class.
From http://www.cplusplus.com/reference/type_traits/is_standard_layout/:
A standard-layout class is a class (defined with class, struct or union) that:
has no virtual functions and no virtual base classes.
has the same access control (private, protected, public) for all its
non-static data members.either has no non-static data members in the most derived class and
at most one base class with non-static data members, or has no base
classes with non-static data members.its base class (if any) is itself also a standard-layout class.
And, has no base classes of the same type as its first non-static
data member.
In you example class C does not match the third rule and so it is not a standard-layout class.
Also, about your question about 'or', it applies only to the second part, i.e. after 'or' the statement is still about standard-layout class object, but without non-static members.
Pointer-interconvertibility: reinterpret_cast derived to base
No, it would be undefined behaviour to attempt to use a pointer casted in such a manner.
[basic.compound] ¶4
Two objects
a
andb
are pointer-interconvertible if:[...] one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, any base class subobject of that object [...]
If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a
reinterpret_cast
.
Because Object
has a non-static data member, it is not valid in standard C++ to reinterpret_cast
its address to any of its base class subobjects and then use the resulting pointer. Even if Object
had no non-static data members, it still would not be valid because it is not a standard-layout class.
Aliasing and pointer-interconvertability
Here
int fn(const X& x, int& p) {
int i = x.k;
p = 2;
return i + x.k;
}
X::k
is int
,p
is a reference to int
. p
can be a reference to x.k
.
On the other hand, here:
int fn(X<struct A>& x, X<struct B>& p) {
int i = x.k;
p.k = 2;
return i + x.k;
}
X<struct A>
and X<struct B>
are distinct types. There is no way to have x
and p
or parts of it refer to the same object.
But what if k is private and X has operator int() const returning k ?
Then nothing changes. Sloppy speaking, you need a reference/pointer to get potential aliasing. For example
struct G {};
struct H { G* g; }
void foo(G* a,H b);
Here b.g
and a
can point to the same G
(note that this is the case no matter if b
is passed by value, reference or pointer). In your example...
template<typename T>
struct X {int k; };
int fn(X<struct A>& x, X<struct B>& p)
.. the only references are x
and p
. They refer to objects of different types, aka different objects.
Is it safe to reinterpret_cast between a Type and a WrapperType?
Reinterpreting T
(that's not a member of Wrapper<T>
) as Wrapper<T>
is never allowed (the F1
example).
On the other hand, I believe reinterpreting Wrapper<T>
as T
is allowed for standard-layout classes: (the F2
example)
[basic.compound]/4.3
Two objects a and b are pointer-interconvertible if:
— one is a standard-layout class object and the other is the first non-static data member of that object, or ...
And right below that:
If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a
reinterpret_cast
. ...
Note that while this rule is symmetrical, it requires both objects to actually exist. If you have a reference to T
pointing to the member of Wrapper<T>
, then you can reinterpret it as Wrapper<T>
(and the other way around). But if it points to a T
object that's not a member of Wrapper<T>
, then it would be UB.
Disclaimer: By "such-and-such reinterpreting is not allowed" I mean that accessing the result of the reinterpret_cast
would cause UB. The cast itself shouldn't cause UB.
Is it possible to get a pointer to one subobject via a pointer to a different, unreleated subobject?
This is perfectly well-defined:
void something(int *x) {
reinterpret_cast<Point*>(x)->y = 42;
}
The Point
object (p
) and its x
member are pointer-interconvertible, from [basic.compound]:
Two objects a and b are pointer-interconvertible if:
- [...]
- one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, any base class subobject of that object ([class.mem]), or:
- [...]
If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a
reinterpret_cast
.
That reinterpret_cast<Point*>(x)
is valid and does end up with a pointer that points to p
. Hence, modifying it directly is fine. As you can see, the standard-layout part and the first non-static data member part are significant.
Although it's not like the compilers in question optimize out the extra load if you pass a pointer to p.y
in and return p.x
instead.
Related Topics
New to Xcode Can't Open Files in C++
Heap Corruption Under Win32; How to Locate
How Is the Std::Tr1::Shared_Ptr Implemented
Writing Function Definition in Header Files in C++
Returning Const Reference to Local Variable from a Function
Why Can a T* Be Passed in Register, But a Unique_Ptr<T> Cannot
At What Point Is It Worth Using a Database
How to Force Cache Coherency on a Multicore X86 Cpu
Conventions for Accessor Methods (Getters and Setters) in C++
Using Boost Thread and a Non-Static Class Function
Receiving Rtsp Stream Using Ffmpeg Library
Is It Legal to Use the Increment Operator in a C++ Function Call
Why Is the Std::Initializer_List Constructor Preferred When Using a Braced Initializer List
Converting Yuv into Bgr or Rgb in Opencv
Linking with Clang++ on Os X Generates Lots of Symbol Not Found Errors
What Is a Reference-To-Pointer
What Happens If 'Throw' Fails to Allocate Memory for Exception Object