我们刚刚将编译器升级到gcc 4.6,现在我们得到了一些警告。目前我们的代码库还没有用c ++ 0x编译的状态,无论如何,我们不想在prod中运行它(至少还没有) - 所以我需要一个修复来删除这个警告。
警告通常是由于以下情况发生的:
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中,上面生成:
警告:类型惩罚指针将破坏严格别名规则
现在删除此错误的一种干净方法是使用union
,但是就像我说的那样,我们不能使用c ++ 0x(因此不受限制的联合),所以我使用了可怕的黑客下面 - 现在警告已经消失了,但是我可能会调用鼻子守护进程吗?
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
。
答案 0 :(得分:2)
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
您可以尝试:
SomeDataPage page;
SomeType *data = new(page.vData) SomeType(); // non-trivial constructor
data->some_member();
答案 1 :(得分:1)
为什么不使用:
SomeType *item = new (page.vData) SomeType();
然后:
item->some_member ();
我不认为工会是最好的方式,也可能有问题。来自gcc docs:
`-fstrict-aliasing'
Allows the compiler to assume the strictest aliasing rules
applicable to the language being compiled. For C (and C++), this
activates optimizations based on the type of expressions. In
particular, an object of one type is assumed never to reside at
the same address as an object of a different type, unless the
types are almost the same. For example, an `unsigned int' can
alias an `int', but not a `void*' or a `double'. A character type
may alias any other type.
Pay special attention to code like this:
union a_union {
int i;
double d;
};
int f() {
a_union t;
t.d = 3.0;
return t.i;
}
The practice of reading from a different union member than the one
most recently written to (called "type-punning") is common. Even
with `-fstrict-aliasing', type-punning is allowed, provided the
memory is accessed through the union type. So, the code above
will work as expected. However, this code might not:
int f() {
a_union t;
int* ip;
t.d = 3.0;
ip = &t.i;
return *ip;
}
这与你的问题有什么关系是很难确定的。我想编译器没有看到SomeType
中的数据与vData
中的数据相同。
答案 2 :(得分:0)
坦白说,我更关心的是SOME_SIZE不够大。但是,使用char*
的任何类型别名 是合法的。所以简单地做reinterpret_cast<T*>(&page.vData[0])
应该没问题。
另外,我会质疑这种设计。除非您正在实施boost::variant
或类似的东西,否则没有太多理由使用它。
答案 3 :(得分:0)
struct SomeDataPage
{
// members
char vData[SOME_SIZE];
};
这对于别名/对齐原因存在问题。首先,此结构的对齐方式不一定与您尝试在其中进行对齐的类型相同。您可以尝试使用GCC属性来强制执行某个对齐:
struct SomeDataPage { char vData[SOME_SIZE] __attribute__((aligned(16))); };
16对齐应该适合我遇到的任何事情。然后,编译器仍然不喜欢你的代码,但如果对齐好,它不会破坏。或者,您可以使用新的C ++ 0x alignof / alignas。
template <class T>
struct DataPage {
alignof(T) char vData[sizeof(T)];
};