1

ストレージ用の小さなクラスを持つための次のコードがあります。

#include <iostream>

template<typename T>
class storage
{
private:
  struct destroy
  {
    T& m_t;
    destroy(T& t) : m_t(t) { }
    ~destroy() { m_t.~T(); }
  };

  char m_c[sizeof(T)];
  void* address() { return &m_c[0]; }

public:
  void set(const T& t) { new (address()) T(t); }

  T get()
  {
    T& t = *static_cast<T*>(address());
    destroy _d(t);
    return t;
  }

};

template<typename T>
class choosable_storage
{
private:
  union
  {
    T*         m_p;
    storage<T> m_storage;
  };
  bool m_direct;

public:
  choosable_storage() : m_direct(false) { }

  void set_direct(const T& t)
  {
    m_direct = true;
    m_storage.set(t);
  }

  void set_indirect(T* const t) { m_p = t; }

  T get()
  {
    if (m_direct) return m_storage.get();
    return *m_p;
  }

};

int main(void)
{
  storage<int> s; // no problems
  s.set(42);
  std::cout << s.get() << std::endl;

  int i = 10;

  choosable_storage<int> c1; // strict aliasing warnings
  c1.set_indirect(&i);
  std::cout << c1.get() << std::endl;

  choosable_storage<int> c2;
  c2.set_direct(i);
  std::cout << c2.get() << std::endl;

  return 0;
}

storage::get()gcc 4.4 は、戻ったときに厳密なエイリアシング規則を破っていることを警告しています。

私の知る限り、私はいかなる規則にも違反していません。私は実際に厳密なエイリアシングに違反していますか、それとも gcc はここでうるさくなっていますか?

そして、厳密なエイリアシングを無効にせずに警告を出さないようにする方法はありますか?

ありがとう

編集:

一方、次の実装では警告が表示されません。

template<typename T>
class storage
{
private:
  struct destroy
  {
    T& m_t;
    destroy(T& t) : m_t(t) { }
    ~destroy() { m_t.~T(); }
    T const& operator()() const { return m_t; }
  };

  char m_c[sizeof(T)];

public:
  void set(const T& t) { new(static_cast<void*>(m_c)) T(t); }

  T get(void) { return destroy(*static_cast<T*>(static_cast<void*>(m_c)))(); }

};

編集:

gcc 4.5 以降は警告を発行しません。つまり、これは厳密なエイリアシング ルールの誤解か、gcc 4.4.x のバグであったようです。

4

2 に答える 2

3

私は実際に厳密なエイリアシングに違反していますか、それともgccはここでうるさくなっていますか?

厳密なエイリアシングルールには2つの異なる解釈があります。

  • char通常の弱い厳密なエイリアシングルール:( / typeを除く)型のパンニングを実行するためにキャストまたはユニオンをunsigned char使用することはできません。必要です(または2回のアクセス)。このようなコードは実際にはあまり合理的ではなく、C(最新の非常にばかげたC標準を除く)およびC++では明示的に禁止されています。memcpyvolatile
  • 異常な強力な厳密なエイリアシングルール:メモリを再利用することはできません。メモリの領域が「動的タイプ」(何?)になると、別のタイプで使用することはできません。したがって、毎回/mallocを呼び出すだけでない限り、アロケータ関数(代替)Cを作成することはできません。mallocfree

強力なルールは明確に定義されておらず、多くの合理的なコードを破り、何らかの理由でGCCメンテナによって選択されました(完全に自己妄想と循環的正当化に基づいています-これは本当に醜いです)。C ++コードを強力で厳密なエイリアシング最適化で機能させるために、G ++のメンテナは、最適化を維持するために、典型的なC ++コード(より自己妄想に基づく)にペシミゼーションを追加しました。

彼らが間違いに気付いたかどうか/いつ気づいたかはわかりません。疑わしい場合は、厳密なエイリアシングを無効にしてください。とにかく、それは非常にマイナーな最適化です。

于 2012-07-21T02:33:33.150 に答える
0

この質問の目的のために、厳密なエイリアシング規則は、基本的に、それ自体の型のポインタ/参照または文字型(charまたはunsignedchar)へのポインタ/参照を介する場合を除いてオブジェクトにアクセスすることは想定されていないことを示しています。

コードには、char型の要素の配列m_cがあり、T型の参照を介してそれにアクセスしようとしています。これは、厳密なエイリアシング違反です。一部のよりエキゾチックなプラットフォームでは、たとえば、m_cがタイプTの要素を保持するように適切に配置されていない場合、これが影響を与える可能性があります。

于 2010-11-13T04:58:43.860 に答える