I membri di una struttura POD o del tipo di layout standard sono garantiti per essere allineati in base ai loro requisiti di allineamento?

Dato un POD-struct (in C ++ 03) o un tipo di layout standard (in C ++ 11), con tutti i membri che hanno un requisito di allineamento fondamentale, è vero che ogni membro è garantito per essere allineato in base al suo requisito di allineamento ?

In altre parole, per tutti i membri m_k in { m0mn } del tipo di layout standard S ,

  struct S { T0 m0; T1 m1; ... TN mn; }; 

è la seguente espressione garantita per valutare a true ?

  (offsetof(S,m_k) % alignof(decltype(S::m_k))) == 0 

Si prega di fornire risposte sia per C ++ 03 che per C ++ 11 e citare le parti rilevanti dello standard. Sarebbe inoltre utile fornire prove a sostegno degli standard C.


La mia lettura dello standard C ++ 03 (ISO / IEC 14882: 2003 (E)) è che è silenzioso riguardo l’allineamento dei membri all’interno di una struttura POD, fatta eccezione per il primo membro. I paragrafi pertinenti sono:

Nella lingua della specifica, un object è una “regione di memorizzazione”:

1.8 Il modello dell’object C + + [intro.object]

1.8 / 1 I costrutti in un programma C + + creano, distruggono, fanno riferimento, accedono e manipolano gli oggetti. Un object è un’area di memoria. …

Gli oggetti vengono assegnati in base al loro requisito di allineamento:

3.9 Tipi [basic.types]

3.9 / 5 I tipi di object hanno requisiti di allineamento (3.9.1, 3.9.2). L’allineamento di un tipo di object completo è un valore intero definito dall’implementazione che rappresenta un numero di byte; un object viene assegnato a un indirizzo che soddisfa i requisiti di allineamento del suo tipo di object.

I tipi fondamentali hanno requisiti di allineamento:

3.9.1 Tipi fondamentali [basic.fundamental]

3.9.1 / 3 Per ciascuno dei tipi di interi con segno, esiste un corrispondente (ma diverso) tipo di intero senza segno: “unsigned char”, “unsigned short int”, “unsigned int” e “unsigned long int”, ciascuno di che occupa la stessa quantità di memoria e ha gli stessi requisiti di allineamento (3.9) del corrispondente tipo intero con segno ; …

Il riempimento può verificarsi a causa di “requisiti di allineamento dell’implementazione”:

9.2 Membri della class [class.mem]

9.2 / 12 I membri di dati non statici di una class (non sindacata) dichiarata senza un identificatore di accesso intermedio sono allocati in modo che i membri successivi abbiano indirizzi superiori all’interno di un object class. L’ordine di allocazione dei membri di dati non statici separati da uno specificatore di accesso non è specificato (11.1). I requisiti di allineamento dell’implementazione potrebbero far sì che due membri adiacenti non vengano allocati immediatamente uno dopo l’altro ; quindi potrebbero essere necessari requisiti per lo spazio per la gestione di funzioni virtuali (10.3) e classi di base virtuali (10.1).

La parola “allocata” in 9.2 / 12 ha lo stesso significato di “allocata” in 3.9 / 5? La maggior parte degli usi di “allocati” nelle specifiche si riferiscono all’allocazione dynamic dello storage, non al layout interno della struttura. L’uso di may in 9.2 / 12 sembra implicare che i requisiti di allineamento di 3.9 / 5 e 3.9.1 / 3 potrebbero non essere strettamente richiesti per i membri della struttura.

Il primo membro di una struttura POD verrà allineato in base al requisito di allineamento della struttura:

9.2 / 17 Un puntatore a un object POD-struct, opportunamente convertito usando un reinterpret_cast, punta al suo membro iniziale (o se quel membro è un campo di bit, quindi all’unità in cui risiede) e viceversa. [ Nota: potrebbe esserci quindi padding senza nome all’interno di un object POD-struct, ma non all’inizio, come necessario per ottenere l’allineamento appropriato. ]

[Enfasi aggiunta in tutte le citazioni sopra.]

Ogni elemento di una struttura POD è esso stesso un object e gli oggetti possono essere allocati solo in conformità con i requisiti di allineamento per tali oggetti. I requisiti di allineamento possono cambiare, tuttavia, a causa del fatto che qualcosa è un sottoobject di un altro object ([basic.align] / 1, 2:

1 I tipi di object hanno requisiti di allineamento (3.9.1, 3.9.2) che pongono restrizioni agli indirizzi a cui un object di quel tipo può essere assegnato. Un allineamento è un valore intero definito dall’implementazione che rappresenta il numero di byte tra gli indirizzi successivi a cui può essere assegnato un dato object. Un tipo di object impone un requisito di allineamento su ogni object di quel tipo; un allineamento più severo può essere richiesto usando lo specificatore di allineamento (7.6.2).

2 Un allineamento fondamentale è rappresentato da un allineamento minore o uguale al più grande allineamento supportato dall’implementazione in tutti i contesti, che è uguale a alignof(std::max_align_t) (18.2). L’allineamento richiesto per un tipo potrebbe essere diverso quando viene utilizzato come tipo di un object completo e quando viene utilizzato come tipo di un object secondario . [ Esempio:

 struct B { long double d; }; struct D : virtual B { char c; } 

Quando D è il tipo di un object completo, avrà un sottoobject di tipo B , quindi deve essere allineato in modo appropriato per un long double . Se D appare come un sottoobject di un altro object che ha anche B come una class base virtuale, il sottobject B potrebbe essere parte di un diverso subobject, riducendo i requisiti di allineamento sull’esempio fine D subobject.- Il risultato dell’operatore alignof riflette il requisito di allineamento del tipo nel caso object completo.

[enfasi aggiunta]

Sebbene gli esempi si riferiscano ad un sotto-object via ereditarietà, il testo normativo si riferisce solo ai sotto-oggetti in generale, quindi credo che si applichino le stesse regole, quindi da una parte si può supporre che ogni sotto-object sia allineato in modo che possa accessibile. D’altra parte, no non puoi necessariamente assumere che sarà lo stesso allineamento che alignof offre l’allineamento.

[Il riferimento è da N4296, ma credo che lo stesso valga per tutte le versioni recenti. C ++ alignof , ovviamente, non aveva affatto l’ alignof , ma credo che si applichi lo stesso principio di base: i membri saranno allineati in modo che possano essere usati, ma quel requisito di allineamento non è necessariamente lo stesso di quando sono usati come oggetti indipendenti.]

Esistono due distinti requisiti di allineamento per ciascun tipo. Uno corrisponde agli oggetti completi, l’altro ai sottooggetti. [Basic.align] / 2:

L’allineamento richiesto per un tipo potrebbe essere diverso quando viene utilizzato come tipo di un object completo e quando viene utilizzato come tipo di un object secondario. [ Esempio :

 struct B { long double d; }; struct D : virtual B { char c; }; 

Quando D è il tipo di un object completo, avrà un sottoobject di tipo B , quindi deve essere allineato in modo appropriato per un long double . Se D appare come un sottoobject di un altro object che ha anche B come una class base virtuale, il sottobject B potrebbe far parte di un sottobobject differente, riducendo i requisiti di allineamento sul sottobject Dfine esempio ] Il risultato dell’operatore alignof riflette il requisito di allineamento del tipo nel caso object completo .

Non riesco a pensare ad alcun esempio specifico per gli oggetti secondari dei membri – e sicuramente non c’è nessuno! -, ma il paragrafo precedente ammette la possibilità che un membro suboject abbia un allineamento più debole di alignof che alignof allineamento, il che fallirebbe la tua condizione.