Frage

Ich habe ein Problem, das durch das Aufbrechen strenger Zeiger -Aliasing -Regel verursacht wurde. Ich habe einen Typ T Das stammt aus einer Vorlage und einem integralen Typ Int der gleichen Größe (wie mit sizeof). Mein Code macht im Wesentlichen Folgendes::

T x = some_other_t;
if (*reinterpret_cast <Int*> (&x) == 0)
  ...

Da T ist ein willkitärer Typ (außer der Größenbeschränkung), die einen Konstruktor haben könnten, ich kann keine Vereinigung machen T und Int. (Dies ist nur in C ++ 0x nur zulässig und wird noch nicht einmal von GCC unterstützt).

Gibt es eine Möglichkeit, das obige Pseudocode umzuschreiben, um die Funktionalität zu bewahren und zu vermeiden, strenge Aliasing -Regel zu brechen? Beachten Sie, dass dies eine Vorlage ist, ich kann nicht kontrollieren T oder Wert von some_other_t; Die Zuordnung und der anschließende Vergleich erfolgen im Vorlagencode.

(Für den Datensatz begann der obige Code auf GCC 4.5, wenn T Enthält alle Bitfelder.)

War es hilfreich?

Lösung

static inline int is_T_0(const T *ob)
{
        int p;
        memcpy(&p, ob, sizeof(int));
        return p == 0;
}

void myfunc(void)
{
    T x = some_other_t;
    if (is_T_0(&x))
        ...

In meinem System optimiert GCC beide weg is_T_0() und memcpy(), was zu nur wenigen Montageanweisungen in führt myfunc().

Andere Tipps

Hast du von gehört boost::optional ?

Ich muss zugeben, dass mir das eigentliche Problem hier unklar bin. Ich erlaube auch in Ort und zu Zerstörung, also könnte ich gut passen, denke ich.

BEARBEITEN:

Ich glaube, ich habe das Problem endlich erfasst: Sie möchten in der Lage sein, viele Objekte an verschiedenen Stellen im Speicher zuzuweisen, und Sie möchten wissen, ob das Gedächtnis an diesem Punkt wirklich ein Objekt enthält oder nicht.

Leider hat Ihre Lösung ein großes Problem: Sie ist falsch. Wenn jemals T kann irgendwie durch a dargestellt werden null Bit -Muster, dann werden Sie denken, dass es sich um einen einheitlichen Speicher handelt.

Sie müssen sich zurückgreifen, um mindestens ein Stück Informationen hinzuzufügen. Nach all dem ist es nicht viel, was nur 3% des Wachstums ausmachen (33 Bit für 4 Bytes).

Sie können zum Beispiel etwas Mimick verwenden boost::optional aber in Array (um den Polsterverlust zu vermeiden).

template <class T, size_t N>
class OptionalArray
{
public:


private:
  typedef unsigned char byte;

  byte mIndex[N/8+1];
  byte mData[sizeof(T)*N]; // note: alignment not considered
};

Dann ist es so einfach:

template <class T, size_t N>
bool OptionalArray<T,N>::null(size_t const i) const
{
  return mIndex[i/8] & (1 << (i%8));
}

template <class T, size_t N>
T& OptionalArray<T,N>::operator[](size_t const i)
{
  assert(!this->null(i));
  return *reinterpret_cast<T*>(mData[sizeof(T)*i]);
}

Hinweis: Der Einfachheit halber habe ich das Problem der Ausrichtung nicht berücksichtigt. Wenn Sie nichts über das Thema Bescheid wissen, lesen Sie darüber, bevor Sie mit Erinnerung fummeln :)

Wie wäre es damit:

Int zero = 0;
T x = some_other_t;
if (std::memcmp(&x, &zero, sizeof(zero)) == 0)

Es mag nicht so effizient sein, aber es sollte die Warnung loswerden.


Addendum #1:

Seit T ist eingeschränkt, die gleiche Größe zu haben wie Int, machen Sie sich zu einem Dummy Bitwise Null -Wert des Typs T und vergleiche direkt damit (anstatt Agaist zu gießen und zu vergleichen Int(0)).

Wenn Ihr Programm Single-Threaded ist, könnten Sie so etwas haben:

template <typename T>
class Container
{
public:
    void foo(T val)
    {
        if (zero_ == val)
        {
            // Do something
        }
    }

private:
    struct Zero
    {
        Zero() {memset(&val, 0, sizeof(val));}
        bool operator==(const T& rhs) const {return val == rhs;}
        T val;
    };
    static Zero zero_;
};

Wenn es Multi-Threaded ist, möchten Sie vermeiden, ein statisches Mitglied zu verwenden zero_, und lassen Sie jede Containerinstanz sein eigenes halten zero_ Mitglied:

template <typename T>
class MTContainer
{
public:
    MTContainer() {memset(zero_, 0, sizeof(zero_));}

    void foo(T val)
    {
        if (val == zero_)
        {
            // Do something
        }
    }

private:
    T zero_;
};

Addendum #2:

Lassen Sie mich das obige Addendum auf eine andere, einfachere Weise einsetzen:

// zero is a member variable and is inialized in the container's constructor
T zero;
std::memset(&zero, 0, sizeof(zero));

T x = some_other_t;
if (x == zero)

Warum nicht einfach:

const Int zero = 0;
if (memcmp(&some_other_t, &zero, sizeof(zero)) == 0)
  /* some_other_t is 0 */

(Möglicherweise möchten Sie versuchen, auch das hinzuzufügen static Qualifikation zu zero Um zu sehen, ob es einen Unterschied leistungsbezogen macht)

Verwenden Sie einen 33-Bit-Computer. ;-P

Es fühlt sich wie ein Hack an, aber anscheinend habe ich eine Lösung gefunden: Verwenden volatile zum Int Casting. Im Wesentlichen bin ich jetzt: Was ich jetzt tue, ist:

T x = some_other_t;
if (*reinterpret_cast <volatile Int*> (&x) == 0)
  ...

Das Problem mit Bitfield T ist jetzt weg. Trotzdem fühle ich mich nicht ganz glücklich darüber volatile ist in C ++-Afaik nicht genau definiert ...

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