gcc: How to use __attribute((__may_alias__)) properly to avoid "derefencing type-punned pointer" warning
17:54 10 Jun 2011

I've got some code that uses type-punning to avoid having to call a member "object"'s constructor and destructor unless/until it's actually necessary to use the object.

It works fine, but under g++ 4.4.3, I get this dreaded compiler warning:

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

My code tries to use gcc's __attribute((__may_alias__)) to let gcc know about the potential aliasing, but gcc doesn't seem to understand what I'm trying to tell it. Am I doing something wrong, or does gcc 4.4.3 just have some problems with the __may_alias__ attribute?

Toy code to reproduce the compiler warning is below:

#include 
#include     // for placement new
#include   // 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 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(_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(_optionalObject._buf)) T();
         _isObjectConstructed = true;
      }
      (reinterpret_cast(_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 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;
}
c++ gcc compiler-warnings strict-aliasing type-punning