2009-07-31 2 views
113

Ho scelto questo in uno dei miei brevi incursioni a reddit:In C++, se throw è un'espressione, qual è il suo tipo?

http://www.smallshire.org.uk/sufficientlysmall/2009/07/31/in-c-throw-is-an-expression/

In sostanza, l'autore fa notare che in C++:

throw "error" 

è espressione. Questo è in realtà abbastanza chiaramente enunciato nello standard C++, sia nel testo principale che nella grammatica. Tuttavia, ciò che non è chiaro (almeno per me) è qual è il tipo di espressione? Ho indovinato "void", ma un po 'di sperimentare con g ++ 4.4.0 e Comeau ceduto questo codice:

void f() { 
    } 

    struct S {}; 

    int main() { 
     int x = 1; 
     const char * p1 = x == 1 ? "foo" : throw S(); // 1 
     const char * p2 = x == 1 ? "foo" : f();  // 2 
    } 

I compilatori avuto nessun problema con // 1 ma barfed su // 2 perché i tipi del operatore condizionale sono diversi. Quindi il tipo di un'espressione throw non sembra essere nullo.

Quindi che cos'è?

Se si risponde, si prega di eseguire il backup delle dichiarazioni con citazioni dallo standard.


questo si è rivelato di non essere così tanto sul tipo di un'espressione tiro da come i condizionali offerte dell'operatore con le espressioni del tiro - cosa che certamente non sapevo nulla prima di oggi. Grazie a tutti quelli che hanno risposto, ma in particolare a David Thornley.

+10

+1 Domanda impressionante. E un modo intelligente per testarlo. –

+1

Questo collegamento sembra rendere abbastanza chiaro che il tipo è determinato dal compilatore per essere qualunque cosa debba essere. – Draemon

+0

L'articolo collegato è stato aggiornato da quando l'ho guardato, e sono sicuro che sia il caso. Tuttavia, non riesco a trovarlo nello standard. –

risposta

94

Secondo lo standard, 5.16 paragrafo 2 primo punto, "Il secondo o il terzo operando (ma non entrambi) è un'espressione di tiro (15.1), il risultato è del tipo dell'altro ed è un valore. " Pertanto, l'operatore condizionale non si preoccupa del tipo di espressione di lancio, ma utilizzerà semplicemente l'altro tipo.

Infatti, 15.1, paragrafo 1 dice esplicitamente "Un'espressione di tiro è di tipo vuoto".

+9

OK - Penso che abbiamo un vincitore. –

+0

Si noti che l'espressione di lancio è un'espressione di assegnazione. Quindi sono un errore di sintassi come argomento per la maggior parte degli operatori. Ovviamente, puoi nasconderli tra parentesi, ma se non vengono ignorati (primo argomento dell'operatore incorporato, ad esempio), si tratta di un errore di tipo. – AProgrammer

+4

Quello che davvero mi sorprende è che hanno pensato a questo caso e hanno fatto accadere qualcosa di ragionevole. – Omnifarious

31

"laterale espressione è di tipo vuoto"

Sezione ISO14882 15

+0

Quindi entrambi g ++ e Comeau non sono in grado di dare un errore per il mio caso // 1? –

+2

@Neil, non proprio perché secondo C++/5.16/2, il secondo e il terzo operando dell'operatore condizionale possono essere di tipo 'void' – mloskot

13

Da [expr.cond.2] (operatore condizionale ?:):

Se il secondo o il terzo operando è di tipo (possibilmente cv-quali fi cato) vuoto, quindi il lvalue-to-rvalue, da matrice a puntatore, e la funzione-to-puntatore conversioni standard vengono eseguiti sul secondo e terzi operandi e uno dei seguenti deve possedere:

- il secondo o il terzo operando (ma non entrambi) è un'espressione di tiro; il risultato è del tipo dell'altro ed è un valore rvalore.

- Sia il secondo che il terzo operando hanno il tipo nullo; il risultato è di tipo void ed è un valore rvalore. [Nota: questo include il caso in cui entrambi gli operandi sono espressioni di lancio.- end nota]

Così, con //1 eri nel primo caso, con //2, si stavano violando "una delle seguenti deve possedere", dal momento che nessuno di loro lo fanno, in questo caso.

3

Si può avere una stampante tipo spit it out for you:

template<typename T> 
struct PrintType; 

int main() 
{ 
    PrintType<decltype(throw "error")> a; 
} 

In sostanza la mancanza di implementazione per PrintType farà sì che il rapporto di errore di compilazione dire:

implicita istanziazione di template definito PrintType<void>

così possiamo effettivamente verificare che throw espressioni sono di tipo void (e sì, le citazioni standard menzionate in altre risposte verificano che questo non sia un risultato specifico dell'implementazione - sebbene gcc abbia difficoltà a stampare informazioni preziose)