2014-08-28 8 views
16

Come procedere nel fare ciò in C++ 11/14 standard? Perché se non sbaglio questo non è un codice standard compatibile con le strutture anonime.Unione anonima e struct

Desidero accedere ai membri nello stesso modo in cui fareste con questo.

template <typename some_type> 
struct vec 
{ 
    union { 
     struct { some_type x, y, z; }; 
     struct { some_type r, g, b; }; 

     some_type elements[3]; 
    }; 
}; 
+0

Se si insiste sulla possibilità di scrivere 'vec t; t.x = 10; 'allora puoi dargli dei membri di riferimento' x', 'y',' z' ecc. e inizializzarli per fare riferimento al membro corrispondente di 'elements', ma questo aumenterà considerevolmente le dimensioni della struttura. Se è possibile tollerare 't.x() = 10', quindi renderli funzioni membro. –

+1

Ma penso che 'vec.x = 42; assert (vec.r == 42); 'comunque non è la garanzia dello standard (ma il compilatore può). – Jarod42

+0

@ Jarod42 N3936 [class.mem]/18: "Se un unione di layout standard contiene due o più strutture di layout standard che condividono una sequenza iniziale comune, e se l'oggetto unione layout standard contiene attualmente uno di questi standard- strutture di layout, è consentito per ispezionare la parte iniziale comune di ognuna di esse ... " – Casey

risposta

12

Sì, né C++ 11 né C++ 14 consentono le strutture anonime. This answer contiene alcuni motivi per cui questo è il caso. È necessario assegnare un nome alle strutture e non possono essere definite all'interno dell'unione anonima.

§9.5/5 [class.union]

... Il membro-specifica di un'unione anonima definisce solo i membri di dati non statici. [Nota: Non è possibile dichiarare tipi annidati, unioni anonime e funzioni all'interno di un'unione anonima. -end nota]

Quindi spostare le definizioni di struct al di fuori dell'Unione.

template <typename some_type> 
struct vec 
{ 
    struct xyz { some_type x, y, z; }; 
    struct rgb { some_type r, g, b; }; 

    union { 
     xyz a; 
     rgb b; 
     some_type elements[3]; 
    }; 
}; 

Ora, ci richiedono di essere some_typestandard di layout perché questo rende tutti i membri del sindacato anonima disposizione compatibile. Here are the requirements per un tipo di layout standard. Questi sono descritti nella sezione §9/7 della norma.

Poi, dal §9.2 [class.mem]

  due standard di layout struct (Clausola 9) tipi sono la layout compatibili se hanno lo stesso numero di non-statico i membri dati e i corrispondenti membri dati non statici (in ordine di dichiarazione) hanno tipi compatibili con il layout (3.9).
  Se un unione di layout standard contiene due o più strutture di layout standard che condividono una sequenza iniziale comune e se l'oggetto unione layout standard contiene attualmente una di queste strutture di layout standard, è consentito ispezionare la parte iniziale comune di ognuno di essi. Due strutture di layout standard condividono una sequenza iniziale comune se i membri corrispondenti hanno tipi di layout compatibili e nessuno dei due membri è un campo di bit o entrambi sono campi di bit con la stessa larghezza per una sequenza di uno o più membri iniziali.

E per il membro di matrice, da §3.9/9 [base.tipi]

... tipi scalari, tipi di classe standard la layout (Clausola 9), array di tali tipi e versioni cv qualificati di questi tipi (3.9.3) sono chiamati collettivamente tipi standard di layout .

Per assicurarsi che some_type è layout standard, aggiungere il seguente nella definizione di vec

static_assert(std::is_standard_layout<some_type>::value, "not standard layout"); 

std::is_standard_layout è definito nell'intestazione type_traits. Ora tutti e 3 i membri del tuo sindacato sono layout standard, le due strutture e l'array sono compatibili con il layout, quindi i 3 membri del sindacato condividono una sequenza iniziale comune, che consente di scrivere e quindi ispezionare (leggere) tutti i membri appartenenti al comune sequenza iniziale (l'intera cosa nel tuo caso).

+1

L'array non è uno standard- struttura del layout, però. –

+0

@ T.C. L'ho appena capito mentre stavo rileggendo dopo la pubblicazione (avrei dovuto farlo prima). Cercare di trovare qualcosa che dice che lo è, sembra sciocco se il tipo è un layout standard, non è garantito che sia un array. – Praetorian

+0

@Praetorian garantisce lo standard che 'elements [0]' sarà uguale a 'a.x' (e' b.r')? Non riesco a trovarlo –

7

Le unioni anonime sono consentite in C++ 11/14. Vedere l'esempio del loro utilizzo a Bjarne Stroustrup's C++11 FAQ

Per quanto riguarda le strutture anonime vedere Why does C++11 not support anonymous structs, while C11 does? e Why does C++ disallow anonymous structs and unions?

Sebbene la maggior parte dei compilatori sostenere le strutture anonime, se si desidera che il codice sia compatibile Standard devi scrivere qualcosa del genere:

template <typename some_type> 
struct vec 
{ 
    union { 
     struct { some_type x, y, z; } s1; 
     struct { some_type r, g, b; } s2; 

     some_type elements[3]; 
    }; 
}; 
5

penso che le altre risposte sorta di mancato il punto della questione:

desidero accedere ai membri allo stesso modo come si farebbe con questo.

In altre parole, la questione è davvero "come faccio io definire un tipo vec in modo conforme agli standard in modo tale che in un oggetto u di quel tipo, u.x, u.r e u.elements[0] si riferiscono tutti alla stessa cosa ?"

Bene, se insiste su quella sintassi ... allora la risposta ovvia è: riferimenti.

Quindi:

template <typename some_type> 
struct vec 
{ 
    vec() = default; 
    vec(const vec& other) : elements{ other.elements[0], other.elements[1], other.elements[2] } {} 

    vec & operator=(const vec &other) { 
     elements[0] = other.elements[0]; 
     elements[1] = other.elements[1]; 
     elements[2] = other.elements[2]; 
     return *this; 
    } 

    some_type elements[3]; 
    some_type &x = elements[0], &y = elements[1], &z = elements[2]; 
    some_type &r = elements[0], &g = elements[1], &b = elements[2];  
}; 

Il primo problema di questo approccio è che avete bisogno di spazio extra per 6 membri di riferimento - che è piuttosto costoso per un piccolo struct tale.

Il secondo problema di questo approccio è che, data const vec<double> v;, v.x è ancora di tipo double &, quindi si potrebbe scrivere v.x = 20; e farlo compilare senza preavviso o errori - solo per ottenere un comportamento indefinito. Piuttosto male.

Così, in subordine, si potrebbe considerare l'utilizzo di funzioni di accesso:

template <typename some_type> 
struct vec 
{ 
    some_type elements[3]; 
    some_type &x() { return elements[0]; } 
    const some_type &x() const { return elements[0]; } 

    some_type &y() { return elements[1]; } 
    const some_type &y() const { return elements[1]; } 

    some_type &z() { return elements[2]; } 
    const some_type &z() const { return elements[2]; } 

    some_type &r() { return elements[0]; } 
    const some_type &r() const { return elements[0]; } 

    some_type &g() { return elements[1]; } 
    const some_type &g() const { return elements[1]; } 

    some_type &b() { return elements[2]; } 
    const some_type &b() const { return elements[2]; } 
}; 

Si dovrebbe scrivere u.x() ecc invece di u.x, ma il risparmio di spazio è notevole, si può anche fare affidamento sul compilatore funzioni generiche di membri speciali, è banalmente copiabile se some_type è (che abilita alcune ottimizzazioni), è un aggregato e quindi può utilizzare la sintassi di inizializzazione aggregata ed è anche const-correct.

Demo. Si noti che sizeof(vec<double>) è 72 per la prima versione e solo 24 per la seconda.

+0

Anche questo è l'approccio più sicuro perché a differenza di union garantisce che 'x() == elementi [0]' –