这个黑客是否删除别名警告UB?

时间:2011-09-20 10:32:42

标签: c++ gcc compiler-warnings placement-new strict-aliasing

我们刚刚将编译器升级到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

4 个答案:

答案 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)];
};