2015-04-23 13 views
20

Il comportamento del seguente codice è ben definito?Riferimento di associazione a un oggetto prima della costruzione

struct X { int i; }; // trivial 
struct Y : X { Y(){} }; // non-trivial 

extern X xobj; 
int& r1 = xobj.i; // #1 
X xobj; 

extern Y yobj; 
Y& r2 = yobj;  // #2 
// int& r3 = yobj.i; // #3 - this is UB according to the standard 
Y yobj; 

Questo codice si ispira l'esempio nello standard C++, cioè elaborare N4140 [class.cdtor]/1.

Questo è ciò che il paragrafo recita:

Per un oggetto con un costruttore non banale, con riferimento a qualsiasi classe membro o base non statica dell'oggetto prima del costruttore comincia risultati di esecuzione in un comportamento indefinito. Per un oggetto con un distruttore non banale , facendo riferimento a qualsiasi membro non statico o classe base dell'oggetto dopo che il distruttore ha terminato i risultati di esecuzione in un comportamento non definito.

Segue un esempio, che mostra come puntatori possono e non possono essere vincolati agli oggetti.

Così intuitivamente sembra che #1 e #2 sono ben definiti, mentre #3 invoca UB se non commentata, ma, in primo luogo, gli esempi non sono normative, secondo, non c'è alcuna menzione di riferimenti nella esempio, e il terzo e il più importante , il paragrafo precedente non implica che altrimenti il ​​comportamento è ben definito. O lo fa? O forse c'è un'altra citazione rilevante nello standard che mi è sfuggita?

Edit: La risposta può (forse) essere sì se gli oggetti hanno una durata di archiviazione statica, ma possono essere anche locale, ad esempio:

struct A { A(){} }; 
struct B { B(A&){} }; 

struct C { 
    B b; 
    A a; 
    C() : b(a) {} 
}; 

int main() { 
    C c; 
} 

In realtà questo è stato l'ispirazione iniziale per questa domanda, vedi Circular dependency in constructor initialization list

+3

"fare riferimento a qualsiasi membro non statico [...] comporta un comportamento non definito" - il riferimento è esattamente ciò che fa un riferimento (da cui il nome). – molbdnilo

+0

@molbdnilo Lo capisco, ma non spiega cosa succede * altrimenti * (ad esempio, riferendosi a ... prima che il costruttore inizi per la classe con un costruttore banale), questo è il punto della domanda. –

+0

"riferirsi ad esso" significa menzionarlo (è la parola inglese "riferirsi"). –

risposta

0

[...] non c'è alcuna menzione di riferimenti nella esempio [...]

.210

Vuoi dire, tranne che per

[...] riferendosi a qualsiasi membro non statico [...]

Dal brano lei cita, direi che questo cause di linea comportamento indefinito:

int& r3 = yobj.i; // #3 

perché siete:

[...] riferendosi a qualsiasi classe membro o base non statici dell'oggetto prima del costruttore comincia esecuzione

Inoltre, per questo [.]:

e il più importante, la sopra paragrafo non implica che altrimenti il ​​comportamento è ben definito.

Hai ragione, non è così:

comportamento indefinito può essere previsto quando presente norma internazionale omette una definizione esplicita di un comportamento o quando un programma utilizza un costrutto errato o dati errati .

+0

# 3 chiama UB, non c'è dubbio. Ma # 2 è (intuitivamente) equivalente a 'Y * p2 = & yobj' che è chiaramente contrassegnato come ben definito nell'esempio standard (ma non nel testo normativo) –

+0

E '? Cosa significa "classe base" allora, se non "yobj"? – Siguza

+0

È possibile che il riferimento ai membri dati non statici e alle classi base sia UB a causa delle classi base virtuali. Il vtable è impostato durante la costruzione, quindi non è possibile accedere a nessun membro o classe base virtuale prima che la costruzione sia iniziata. – dyp

2

Il comportamento del n. 2 è decisamente ben definito. Come già detto da @dyp, il punto rilevante è in [basic.life]:

enter image description here

Binding la glvalue yobj ad un riferimento va bene, dal momento che la sua conservazione dura per tutta la durata del programma ([di base. stc.static]/1) e il riferimento è legato ad un oggetto valido - aliveness aside - che soddisfa il requisito in ([dcl.ref]/5). Analogamente, per il secondo esempio che hai mostrato, purché non venga eseguita alcuna operazione sui membri del subobject A, il paragrafo precedente si applica anche perché il costruttore di C viene chiamato sulla memoria allocata this fa riferimento a.

Problemi correlati