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_assertvolontà fallirà. Considerando che la modifica di Fooo Barda cui ereditare invece Base2eviterà 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 Barcose contenga la classe base del tipo di foo, o viceversa.
Anzi, se non altro, mi aspetterei che &foosia uguale all'indirizzo Bardell'istanza che lo contiene, poiché è necessario che si trovi in altre situazioni (1) . Dopotutto, non sto facendo nulla di stravagante con l' virtualereditarietà, le classi di base sono vuote a prescindere e la compilazione con Base2mostra 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 StandardLayoutTypes (sebbene Bar, sopra, non sia un StandardLayoutType).
Base *a = new Bar(); Base *b = a->foo;cona==b, maaebsono chiaramente oggetti diversi (forse con diverse sostituzioni di metodi virtuali).