reinterpret_cast对模板聚合类型指针的安全性

时间:2015-01-31 18:14:10

标签: c++ casting

我想知道以下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的说法,似乎有合法使用的声明涵盖了我正在尝试做的事情......

  

键入别名   当对T1类型的对象的指针或引用是reinterpret_cast(或C样式强制转换)为指针或对不同类型T2的对象的引用时,强制转换总是成功,但只有T1和T1都可以访问结果指针或引用和T2是标准布局类型,其中之一是真的:

     

...

     

T2是聚合类型或联合类型,它将上述类型之一保存为元素或非静态成员(包括递归地,包含联合的子聚合和非静态数据成员的元素):这使得它安全地从结构的第一个成员和从联合的元素转换到包含它的结构/联合。

我很欣赏看起来我对此的看法不对。这不是我所关心的。我只想知道我所做的事情是否安全/合法。提前感谢您的帮助。

2 个答案:

答案 0 :(得分:4)

  

似乎有合法使用的陈述涵盖了我试图做的事情......

这不是那个例外所说的或意味着什么。这个例外说明了

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(),因为编译器知道{ {1}}和s不可能指向相同的内存区域。即使您可能尝试将其称为

t

答案 1 :(得分:2)

根据标准,类型别名是否合法,您可能还有其他问题。

  

我想Container<T>的每个实例都有相同的布局和   大小所以它应该不是问题,对吗?

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