L'ottimizzazione della base vuota è ottima. Tuttavia, viene fornito con la seguente limitazione:
L'ottimizzazione della base vuota è vietata se una delle classi di base vuote è anche il tipo o la base del tipo del primo membro di dati non statico, poiché i due oggetti secondari di base dello stesso tipo devono avere indirizzi diversi all'interno della rappresentazione dell'oggetto del tipo più derivato.
Per spiegare questa limitazione, considerare il seguente codice. La static_assert
volontà fallirà. Considerando che la modifica di Foo
o Bar
da cui ereditare invece Base2
eviterà l'errore:
#include <cstddef>
struct Base {};
struct Base2 {};
struct Foo : Base {};
struct Bar : Base {
Foo foo;
};
static_assert(offsetof(Bar,foo)==0,"Error!");
Capisco questo comportamento completamente. Quello che non capisco è perché esiste questo comportamento particolare . È stato ovviamente aggiunto per una ragione, dal momento che è un'aggiunta esplicita, non una svista. Qual è una logica per questo?
In particolare, perché i due oggetti secondari di base dovrebbero avere indirizzi diversi? In quanto sopra, Bar
è un tipo ed foo
è una variabile membro di quel tipo. Non vedo il motivo per cui la classe base delle Bar
cose contenga la classe base del tipo di foo
, o viceversa.
Anzi, se non altro, mi aspetterei che &foo
sia uguale all'indirizzo Bar
dell'istanza che lo contiene, poiché è necessario che si trovi in altre situazioni (1) . Dopotutto, non sto facendo nulla di stravagante con l' virtual
ereditarietà, le classi di base sono vuote a prescindere e la compilazione con Base2
mostra che nulla si rompe in questo caso particolare.
Ma chiaramente questo ragionamento non è corretto in qualche modo, e ci sono altre situazioni in cui questa limitazione sarebbe richiesta.
Diciamo che le risposte dovrebbero essere per C ++ 11 o più recenti (attualmente sto usando C ++ 17).
(1) Nota: EBO è stato aggiornato in C ++ 11 e, in particolare, è diventato obbligatorio per StandardLayoutType
s (sebbene Bar
, sopra, non sia un StandardLayoutType
).
Base *a = new Bar(); Base *b = a->foo;
cona==b
, maa
eb
sono chiaramente oggetti diversi (forse con diverse sostituzioni di metodi virtuali).