Frage

Ich habe einen Code, der Typen-Punning verwendet, um zu vermeiden, dass ein Konstruktor und Destruktor eines Mitglieds "Objekt" bezeichnet werden muss, es sei denn,/bis es tatsächlich notwendig ist, das Objekt zu verwenden.

Es funktioniert gut, aber unter G ++ 4.4.3 bekomme ich diese gefürchtete Compiler -Warnung:

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

Mein Code versucht, GCCs __Attribute ((__ May_alias__)) zu verwenden, um GCC über das potenzielle Aliasing zu informieren, aber GCC scheint nicht zu verstehen, was ich versuche, es zu sagen. Mache ich etwas falsch oder hat GCC 4.4.3 nur einige Probleme mit dem Attribut __may_alias__?

Spielzeugcode zur Reproduktion der Compiler -Warnung ist unten:

#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;
}
War es hilfreich?

Lösung

Ich denke, die typedef verwirrt GCC. Diese Art von Attributen scheinen am besten zu funktionieren, wenn sie direkt auf variable Definitionen angewendet werden.

Diese Version Ihrer Klasse funktioniert für mich (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.]

Andere Tipps

Ich würde argumentieren, dass Ihre enthaltende Klasse nur ein Zeichen für ausreichende Größe enthält, um Ihr Mitglied "Objekt" zu enthalten, und dann die Platzierung neu zu initialisieren, um oben in der Zeichenanordnung zu initialisieren. Das hat den Vorteil, sowohl spezifizierende als auch Cross-Compiler-konforme zu sein. Das einzige Problem ist, dass Sie die Größe in den Zeichen Ihres Mitgliedsobjekts kennen müssen, was Sie möglicherweise in Schwierigkeiten bringen.

Gibt es einen Grund, warum Sie das Mitglied kein Zeiger haben und neue und löschen können?

Was ist, wenn Sie ersetzen _isObjectConstructed mit einem Zeiger auf das Objekt:

class Lightweight
{
public:
   Lightweight() : object(NULL) {/* empty */}

   ~Lightweight()
   {
      // call object's destructor, only if we ever constructed it
      if (object) object->~T();
   }

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

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

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

   T *object;
};

Hinweis, keine GCC -Erweiterung, nur reine C ++ - Code.

Verwendung einer T* anstelle einer bool wird nicht einmal machen Lightweight Wer größer!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top