10

コンパイラをgcc4.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にあります、私たちは最近これを変更しただけだと思います(コンパイラの問題である可能性は常に低いです)、しかし問題はまだ残っています。

編集:さらなる調査により、いくつかの興味深い情報が得られました。コードが次のように2行に分割されている場合、キャストを実行してメンバー関数を1行で呼び出すことが、警告の原因であるようです。

SomeType* ptr = reinterpret_cast<SomeType*>(page.vData);
ptr->some_method();

これは実際には警告を生成しません。その結果、ideoneの簡単な例に欠陥があり、さらに重要なことに、上記のハックでは警告が修正されません。修正する唯一の方法は、キャストから関数呼び出しを分割することです。キャストはとして残すことができます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;
      }

これが問題にどのように関連しているかを判断するのは難しいです。SomeTypeコンパイラは、のデータと同じようにのデータを認識していないと思いvDataます。

于 2011-09-20T10:42:30.670 に答える
0
struct SomeDataPage
{
  // members
  char vData[SOME_SIZE];
};

これは、エイリアシング/アライメントの理由で問題があります。1つは、この構造体の配置が、その中でしゃれようとしているタイプと必ずしも同じではないということです。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 に答える