Placement-new vs gcc 4.4.3 strict- правила псевдонима

У меня есть код, который я успешно использовал в течение нескольких лет для реализации "вариантного типа". объект »; то есть объект C ++, который может содержать значения различных типов, но использует (приблизительно) столько памяти, сколько самый большой из возможных типов. Код похож по духу на tagged-union, за исключением того, что он также поддерживает типы данных, не относящиеся к POD. Он выполняет эту магию, используя буфер символов, размещение new / delete и reinterpret_cast <>.

Недавно я попытался скомпилировать этот код под gcc 4.4.3 (с -O3 и -Wall) и получил множество предупреждений, подобных этому :

warning: dereferencing type-punned pointer will break strict-aliasing rules

Из того, что я прочитал, это указывает на то, что новый оптимизатор gcc может генерировать «ошибочный» код, чего я, очевидно, хотел бы избежать.

Я вставил «игрушечную версию» своего кода ниже; могу ли я что-нибудь сделать со своим кодом, чтобы сделать его более безопасным в gcc 4.4.3, при этом все еще поддерживая типы данных, не относящиеся к POD? Я знаю, что в крайнем случае я всегда могу скомпилировать код с -fno-strict-aliasing, но было бы неплохо иметь код, который не ломается при оптимизации, поэтому я бы не стал этого делать.

(Примечание что я бы не хотел вводить в кодовую базу зависимость boost или C ++ 0X, поэтому, хотя решения boost / C ++ 0X интересны, я бы предпочел что-то более старомодное)

#include <new>

class Duck
{
public:
   Duck() : _speed(0.0f), _quacking(false) {/* empty */}
   virtual ~Duck() {/* empty */}  // virtual only to demonstrate that this may not be a POD type

   float _speed;
   bool _quacking;
};

class Soup
{
public:
   Soup() : _size(0), _temperature(0.0f) {/* empty */}
   virtual ~Soup() {/* empty */}  // virtual only to demonstrate that this may not be a POD type

   int _size;
   float _temperature;
};

enum {
   TYPE_UNSET = 0,
   TYPE_DUCK,
   TYPE_SOUP
};

/** Tagged-union style variant class, can hold either one Duck or one Soup, but not both at once. */
class DuckOrSoup
{
public:
   DuckOrSoup() : _type(TYPE_UNSET) {/* empty*/}
   ~DuckOrSoup() {Unset();}

   void Unset() {ChangeType(TYPE_UNSET);}
   void SetValueDuck(const Duck & duck) {ChangeType(TYPE_DUCK); reinterpret_cast<Duck*>(_data)[0] = duck;}
   void SetValueSoup(const Soup & soup) {ChangeType(TYPE_SOUP); reinterpret_cast<Soup*>(_data)[0] = soup;}

private:
   void ChangeType(int newType);

   template <int S1, int S2> struct _maxx {enum {sz = (S1>S2)?S1:S2};};
   #define compile_time_max(a,b) (_maxx< (a), (b) >::sz)
   enum {STORAGE_SIZE = compile_time_max(sizeof(Duck), sizeof(Soup))};

   char _data[STORAGE_SIZE];
   int _type;   // a TYPE_* indicating what type of data we currently hold
};

void DuckOrSoup :: ChangeType(int newType)
{
   if (newType != _type)
   {
      switch(_type)
      {
         case TYPE_DUCK: (reinterpret_cast<Duck*>(_data))->~Duck(); break;
         case TYPE_SOUP: (reinterpret_cast<Soup*>(_data))->~Soup(); break;
      }
      _type = newType;
      switch(_type)
      {
         case TYPE_DUCK: (void) new (_data) Duck();  break;
         case TYPE_SOUP: (void) new (_data) Soup();  break;
      }
   }
}

int main(int argc, char ** argv)
{
   DuckOrSoup dos;
   dos.SetValueDuck(Duck());
   dos.SetValueSoup(Soup());
   return 0;
}
6
задан Jeremy Friesner 23 November 2010 в 23:02
поделиться