2012-12-17 17 views
7

Nell'esempio seguente, Foo non sta facendo ciò che è previsto, ma non riesco a capire perché questo è permesso di compilare.Utilizzo del riferimento come tipo di modello

#include <string> 
#include <iostream> 

typedef std::string& T; 

T Foo(int & i) 
{ 
    return T(i); 
} 

int main() 
{ 
    int a = 1; 
    std::string & s = Foo(a); 
} 

ho scoperto questo con i modelli, ma il typedef dimostra che la sua estranei a modelli. Inutile dire che s non è una stringa valida qui. Penserei che la costruzione del valore nel ritorno di Foo produrrebbe un errore di compilazione.

Cosa mi manca qui?

+0

Non terribilmente scioccante la tua specializzazione non viene chiamata se stai invocando 'Foo (a);' e mi aspetto la specializzazione 'Foo ()' per essere licenziato. – WhozCraig

+0

@WhozCraig Sono d'accordo, ero disperata perché non aveva senso. – JaredC

+0

Fa, ovviamente, sparare, se invochi direttamente 'Foo ()', ma non è proprio quello che stai alla fine cercando, senza dubbio. – WhozCraig

risposta

6

Prima di tutto, è da notare che il problema non ha in realtà alcun rapporto con i modelli, perché questo codice viene compilato un pozzo:

typedef std::string& T; 
T Foo(int& i) { 
    return T(i); 
} 

La ragione per cui che questo compila è che la dichiarazione return è equivalente a

return reinterpret_cast<T>(i); 

nel caso T è un membro di riferimento. ... e questo, ovviamente, compila: hai promesso di sapere cosa stavi facendo e hai chiesto gentilmente al compilatore di crederti.

OK, trovato presso 5.2.3 [expr.type.conv] paragrafo 1:

... Se la lista espressione è una singola espressione, l'espressione tipo di conversione è equivalente (in definedness, e se definito nel significato) alla corrispondente espressione cast (5.4). ...

... e 5.4 [expr.cast] paragrafo 4:

Le conversioni effettuate da [altre forme di calchi] un reinterpret_cast (5.2.10) [...] può essere eseguita utilizzando la notazione cast di conversione di tipo esplicita. [...]

(le elisioni riguardano casi che coinvolgono utente tipo definito, built-in conversioni di tipo, const conversioni, etc.)

+0

Puoi espandere su * perché * 'reinterpret_cast' viene chiamato in questo caso quando T è un riferimento? – JaredC

+0

Wow, è sottile, specialmente quando si usano i modelli. Vedo una domanda successiva sul divieto di un tipo di riferimento per 'T' in questo caso ... – JaredC

+0

vedere la fine della mia risposta per come proibirlo –

5

Questo non ha nulla a che fare con i modelli, si ottiene lo stesso risultato se T è solo un typedef per std::string& piuttosto che un parametro di modello dedotto:

#include <string> 

typedef std::string& T; 

T Foo(int & i) 
{ 
    return T(i); 
} 

int main() 
{ 
    int a = 1; 
    std::string & s = Foo(a); 
} 

risposta di Dietmar mi ha fatto capire questa può essere ulteriormente semplificato per:

#include <string> 

typedef std::string& T; 

int main() 
{ 
    int a = 1; 
    std::string & s = T(a); 
} 

dove T(a) è lo stesso come il cast (T)a cioè (std::string&)a che (secondo le regole di 5,4 [expr.cast]) farà un const_cast se è valido (che non è) o un static_cast se è valido (che non è) o un static_cast seguita da un const_cast se è valido (che non è) o un reinterpret_cast se è valido (quale è) o un reinterpret_cast seguita da un const_cast se è valido, altrimenti la l'espressione è mal formata.

Quindi, come ha detto Dietmar, è lo stesso di fare un reinterpret_cast, cioè

std::string & s = reinterpret_cast<std::string&>(a); 

Trovo abbastanza sorprendente che il codice originale compila, ma in quanto è la stessa di quella linea di cui sopra, è consentito per la compilazione . Tuttavia, usare il risultato del cast è un comportamento indefinito.

Per evitare la sorpresa in cui T(a) è equivalente a un cast, utilizzare la nuova sintassi di inizializzazione uniforme C++ 11, T{a}, che è sempre un'inizializzazione, non un'espressione di trasmissione.

La grande domanda, investigare e rispondere mi ha mostrato un nuovo trucchetto di cui non ero a conoscenza in precedenza, grazie a JaredC e Dietmar per il nuovo pezzo di conoscenza!

+0

È opportuno modificare la domanda per il controllo remoto dei modelli (dal momento che 2 persone hanno commentato fino ad ora)? O dovrei partire così com'è? – JaredC

+0

Penso che sia OK modificarlo ... la domanda è interessante, quindi ha senso chiarirlo per rimuovere l'aringa rossa riguardante i template. –

+0

@ JaredC: Sono d'accordo con Jonathan. Naturalmente, vorrebbe anche dire che le risposte sono state modificate per tenere conto della domanda modificata;) –

Problemi correlati