我们刚刚将编译器升级到gcc 4.6,现在我们遇到了一些警告。目前我们的代码库还没有达到可以用c++0x编译的状态,而且无论如何,我们也不想在生产环境中运行它(至少还没有),因此我需要一个解决方法来消除这个警告。
这些警告通常是因为类似于以下情况而出现的:
struct SomeDataPage
{
// members
char vData[SOME_SIZE];
};
后来,它被以下方式使用
SomeDataPage page;
new(page.vData) SomeType(); // non-trivial constructor
为了读取、更新和返回以下转换,以前使用过以下方式
reinterpret_cast<SomeType*>(page.vData)->some_member();
在4.4版本中这是可以的,在4.6版本中上述代码会生成以下警告:
warning: type punned pointer will break strict-aliasing rules
现在,消除此错误的一种干净方法是使用union
。然而,正如我所说,我们不能使用c++0x(因此无限制联合),因此我采用了下面这个可怕的Hack - 现在警告已经消失了,但我有可能会调用鼻子守护程序吗?
static_cast<SomeType*>(reinterpret_cast<void*>(page.vData))->some_member();
这似乎可以正常工作(请参见此处的简单示例:http://www.ideone.com/9p3MS),并且不会产生警告,那么在C++0x之前使用这种方法是否可以(并非从风格角度考虑)?
注意:通常我不想使用-fno-strict-aliasing
...
编辑:看来我错了,4.4版也有同样的警告,我猜我们最近才发现了这个问题的变化(这始终不太可能是编译器的问题),但问题仍然存在。
编辑:进一步的调查得出了一些有趣的信息,似乎将转换和调用成员函数放在同一行中导致了警告,如果将代码拆分为以下两行,则没有警告:
SomeType* ptr = reinterpret_cast<SomeType*>(page.vData);
ptr->some_method();
实际上,这并不会产生警告。因此,在ideone上的我的简单示例有缺陷,更重要的是我上面的hack无法修复这个警告,唯一的解决方法是将函数调用与强制转换分开 - 然后可以将强制转换保留为reinterpret_cast
。
new(page.vData) SomeType()
之后,位于与page.vData
相同位置的对象的实际类型是SomeType
,因为这是存储在那里的最后一件事情,因此类型游戏是合法的。尽管转换触发的警告可能不确定您是否已经完成了放置新操作。如果我的判断没有错的话,那么假设GCC中没有错误,那么可怕的黑客应该也没问题(因为当然GCC对reinterpret_cast
的定义使得结果指针相同)。不过我不确定。 - Steve Jessop