reinterpret_cast 在指向模板聚合类型的指针上的安全性

Safety of reinterpret_cast on pointer to template aggregate type

我想知道我接下来对 reinterpret_cast 的使用是否是未定义的行为。

给定模板聚合,例如...

template<typename T>
struct Container
{
  Container(T* p) : ptr(p) { }
  ...
  T* ptr;
};

... 和类似 ...

的类型层次结构
struct A { };
struct B : A { };

以下转换是否安全,因为 BA 的动态类型 ...

Container<B>* b = new Container<B>( new B() );
Container<A>* a = reinterpret_cast<Container<A>*>(b);

...至于我现在可以安全地使用 a->ptr 及其(可能是虚拟的)成员吗?

我使用它的代码可以很好地编译和执行(Clang,OS X),但我担心我已经放置了一颗定时炸弹。我想 Container<T> 的每个实例都共享相同的布局和大小,所以这应该不是问题,对吧?

看看 cppreference.com 关于 reinterpret_cast 的说法,似乎有一个合法使用的声明涵盖了我正在尝试做的事情...

Type aliasing When a pointer or reference to object of type T1 is reinterpret_cast (or C-style cast) to a pointer or reference to object of a different type T2, the cast always succeeds, but the resulting pointer or reference may only be accessed if both T1 and T2 are standard-layout types and one of the following is true:

...

T2 is an aggregate type or a union type which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions): this makes it safe to cast from the first member of a struct and from an element of a union to the struct/union that contains it.

我很感激我在这件事上似乎走错了路。那不是我关心的。我只想知道我所做的是否安全/合法。在此先感谢您的帮助。

无论类型别名是否符合标准,您可能还有其他问题。

I guess every instance of Container<T> shares the same layout and size so it shouldn't be a problem, right?

实际上,并非 Container<T> 的每个实例都共享相同的布局!如 this question 中所述,模板成员仅在使用时创建,因此如果每种类型使用不同的成员,您的 Container<A>Container<B> 可能具有不同的内存布局。

there seems to be a statement for legal use that covers what I'm trying to do ...

那不是那个例外的意思。该异常表示 given

struct S { int i; } s;

您可以使用 *reinterpret_cast<int *>(&s) 访问 s.i

您尝试执行的操作没有类似的例外情况。你试图做的事情在 C++ 中根本无效。即使是下面的也是无效的:

struct S { int i; };
struct T { int i; };
int f(S s) { return ((T &) s).i; }

并且编译器会基于您不会编写那样的代码的假设进行优化。

对于当前编译器在 运行 时失败的实际示例:

#include <cstdlib>
struct S { int i; };
struct T { int i; };
void f(S *s, T *t) { int i = s->i; t->i++; if (s->i == i) std::abort(); }

这里,GCC优化掉了检查s->i == i(GCC 4.9.2,命令行选项中有-O2),无条件调用std::abort(),因为编译器知道st 不可能指向相同的内存区域。即使您可能会尝试将其称为

int main() { S s = { 0 }; f(&s, reinterpret_cast<T *>(&s)); }