3

オブジェクトを実際に使用する必要がある場合を除き、メンバーの「オブジェクト」のコンストラクターとデストラクタを呼び出す必要がないように、タイプパニングを使用するコードがあります。

正常に動作しますが、g++ 4.4.3 では、次の恐ろしいコンパイラ警告が表示されます。

jaf@jeremy-desktop:~$ g++ -O3 -Wall puns.cpp 
puns.cpp: In instantiation of ‘Lightweight<Heavyweight>’:
puns.cpp:68:   instantiated from here 
puns.cpp:12: warning: ignoring attributes applied to ‘Heavyweight’ after definition
puns.cpp: In destructor ‘Lightweight<T>::~Lightweight() [with T = Heavyweight]’:
puns.cpp:68:   instantiated from here
puns.cpp:20: warning: dereferencing type-punned pointer will break strict-aliasing rules
puns.cpp: In member function ‘void Lightweight<T>::MethodThatGetsCalledRarely() [with T = Heavyweight]’:
puns.cpp:70:   instantiated from here
puns.cpp:36: warning: dereferencing type-punned pointer will break strict-aliasing rules

私のコードは gcc の __attribute((__may_alias__)) を使用して gcc にエイリアシングの可能性を知らせようとしていますが、gcc は私が伝えようとしていることを理解していないようです。私は何か間違ったことをしていますか、それとも gcc 4.4.3 には __may_alias__ 属性に関する問題がありますか?

コンパイラの警告を再現するおもちゃのコードは次のとおりです。

#include <stdio.h>
#include <memory>    // for placement new
#include <stdlib.h>  // for rand()

/** Templated class that I want to be quick to construct and destroy.
  * In particular, I don't want to have T's constructor called unless
  * I actually need it, and I also don't want to use dynamic allocation.
  **/
template<class T> class Lightweight
{
private:
   typedef T __attribute((__may_alias__)) T_may_alias;

public:
   Lightweight() : _isObjectConstructed(false) {/* empty */}

   ~Lightweight()
   {
      // call object's destructor, only if we ever constructed it
      if (_isObjectConstructed) (reinterpret_cast<T_may_alias *>(_optionalObject._buf))->~T_may_alias();
   }

   void MethodThatGetsCalledOften()
   {
      // Imagine some useful code here
   }

   void MethodThatGetsCalledRarely()
   {
      if (_isObjectConstructed == false)
      {
         // demand-construct the heavy object, since we actually need to use it now
         (void) new (reinterpret_cast<T_may_alias *>(_optionalObject._buf)) T();
         _isObjectConstructed = true;
      }
      (reinterpret_cast<T_may_alias *>(_optionalObject._buf))->DoSomething();
   }

private:
   union {
      char _buf[sizeof(T)];
      unsigned long long _thisIsOnlyHereToForceEightByteAlignment;
   } _optionalObject;

   bool _isObjectConstructed;
};

static int _iterationCounter = 0;
static int _heavyCounter     = 0;

/** Example of a class that takes (relatively) a lot of resources to construct or destroy. */
class Heavyweight
{
public:
   Heavyweight()
   {
      printf("Heavyweight constructor, this is an expensive call!\n");
      _heavyCounter++;
   }

   void DoSomething() {/* Imagine some useful code here*/}
};

static void SomeMethod()
{
   _iterationCounter++;

   Lightweight<Heavyweight> obj;
   if ((rand()%1000) != 0) obj.MethodThatGetsCalledOften();
                      else obj.MethodThatGetsCalledRarely();
}

int main(int argc, char ** argv)
{
   for (int i=0; i<1000; i++) SomeMethod();
   printf("Heavyweight ctor was executed only %i times out of %i iterations, we avoid %.1f%% of the ctor calls!.\n", _heavyCounter, _iterationCounter, 100.0f*(1.0f-(((float)_heavyCounter)/((float)_iterationCounter))));
   return 0;
}
4

3 に答える 3

5

typedefはGCCを混乱させていると思います。これらの種類の属性は、変数の定義に直接適用する場合に最適に機能するようです。

あなたのクラスのこのバージョンは私のために働きます(GCC 4.6.0):

template<class T> class Lightweight
{
private:
  //  typedef T __attribute((__may_alias__)) T_may_alias;

public:
  Lightweight() : _isObjectConstructed(false) {/* empty */}

  ~Lightweight()
  {
    // call object's destructor, only if we ever constructed it
    if (_isObjectConstructed) {
      T * __attribute__((__may_alias__)) p
        = (reinterpret_cast<T *>(_optionalObject._buf));
      p->~T();
    }
  }

  void MethodThatGetsCalledOften()
  {
    // Imagine some useful code here
  }

  void MethodThatGetsCalledRarely()
  {
    T * __attribute__((__may_alias__)) p
      = (reinterpret_cast<T *>(_optionalObject._buf));
    if (_isObjectConstructed == false)
      {
        // demand-construct the heavy object, since we actually need to use it now

        (void) new (p) T();
        _isObjectConstructed = true;
      }
      p->DoSomething();
  }

  [etc.]
于 2011-06-11T00:01:08.360 に答える
2

私は、あなたの包含クラスがあなたのメンバー「オブジェクト」を含むのに十分なサイズのchar配列を含むようにし、次に配置newを使用してchar配列の上に初期化することを主張します。これには、仕様に準拠しているだけでなく、クロスコンパイラーであるという利点があります。唯一の問題は、メンバーオブジェクトの文字のサイズを知っている必要があることです。これにより、問題が発生する可能性があります。

メンバーをポインタにして、newとdeleteを使用できない理由はありますか?

于 2011-07-14T22:17:37.217 に答える
2

_isObjectConstructedオブジェクトへのポインタに置き換えるとどうなるでしょうか:

クラス軽量
{
公衆:
   Lightweight() : object(NULL) {/* 空 */}

   〜軽量化()
   {
      // オブジェクトを構築したことがある場合にのみ、オブジェクトのデストラクタを呼び出します
      if (オブジェクト) object->~T();
   }

   void MethodThatGetsCalledOften()
   {
      // ここに便利なコードを想像してみてください
   }

   void MethodThatGetsCalledRarely()
   {
      if (!オブジェクト)
      {
         // 実際に使用する必要があるため、重いオブジェクトを要求構築します
         object = new (_optionalObject._buf) T();
      }
      object->DoSomething();
   }

プライベート:
   組合{
      char _buf[sizeof(T)];
      unsigned long long _thisIsOnlyHereToForceEightByteAlignment;
   } _optionalObject;

   T *オブジェクト;
};

GCC 拡張ではなく、純粋な C++ コードのみであることに注意してください。

a のT*代わりに aを使用しboolても、大きくなりませんLightweight!

于 2011-12-13T15:33:17.200 に答える