10

我们刚刚将编译器升级到 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 中,上述生成:

警告:类型双关指针会破坏严格的别名规则

现在消除此错误的一种干净方法是使用 a 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.

4

4 回答 4

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();
于 2011-12-13T15:17:04.297 回答
1

为什么不使用:

SomeType *item = new (page.vData) SomeType();

接着:

item->some_member ();

我不认为工会是最好的方式,它也可能有问题。来自 gcc 文档:

`-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;
      }

这与您的问题有何关系很难确定。我猜编译器看到的数据SomeTypevData.

于 2011-09-20T10:42:30.670 回答
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)];
};
于 2011-09-20T15:08:23.857 回答
0

坦率地说,我会更担心 SOME_SIZE 不够大。但是,使用 . 为任何类型起别名合法的char*。所以简单地做reinterpret_cast<T*>(&page.vData[0])应该就好了。

另外,我会质疑这种设计。除非您正在实施boost::variant或类似的东西,否则没有太多理由使用它。

于 2011-09-20T10:59:28.353 回答