Ich habe Code, den ich seit einigen Jahren erfolgreich verwendet, um ein "variant-type object" zu implementieren; das heißt, ein C++ - Objekt, das Werte verschiedener Typen enthalten kann, aber nur so viel Speicher verwendet wie der größte der möglichen Typen. Der Code ähnelt im Wesentlichen einer markierten Union, außer dass er auch Nicht-POD-Datentypen unterstützt. Es erreicht diese Magie, indem es einen Zeichenpuffer verwendet, Placement new/delete und reinterpret_cast <>.Placement-new vs gcc 4.4.3 Strict-Aliasing-Regeln
ich diesen Code unter gcc vor kurzem versucht 4.4.3 kompilieren (mit O3 und -Wall) und bekam viele Warnungen wie folgt aus:
warning: dereferencing type-punned pointer will break strict-aliasing rules
Von dem, was ich gelesen habe, ist dies ein Zeichen dass der neue Optimierer des GCC 'fehlerhaften' Code erzeugt, den ich offensichtlich vermeiden möchte.
Ich habe unten eine "Spielzeugversion" meines Codes eingefügt; Gibt es irgendetwas, was ich mit meinem Code tun kann, um es unter gcc 4.4.3 sicherer zu machen, während ich immer noch Nicht-POD-Datentypen unterstütze? Ich weiß, dass ich als letzten Ausweg immer den Code mit -no-strict-aliasing kompilieren konnte, aber es wäre schön Code zu haben, der nicht unter Optimierung bricht, also würde ich das lieber nicht machen.
(Beachten Sie, dass ich vermeiden möchte, eine Erhöhung oder C++ 0X-Abhängigkeit in die Codebasis einzuführen, also, während Boost/C++ 0X-Lösungen interessant sind, würde ich etwas etwas mehr altmodisch bevorzugen)
#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;
}
Das ist Freakish Code ... –
Haben Sie diesen Code an das GCC-Team eingereicht, vielleicht als Fehlerbericht? – curiousguy