In C ++, se throw è un'espressione, qual è il suo tipo?


115

L'ho raccolto in una delle mie brevi incursioni su reddit:

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

Fondamentalmente, l'autore sottolinea che in C ++:

throw "error"

è un'espressione. Questo è effettivamente spiegato abbastanza chiaramente 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 sperimentazione con g ++ 4.4.0 e Comeau ha prodotto 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 non hanno avuto problemi con // 1 ma hanno bloccato // 2 perché i tipi nell'operatore condizionale sono diversi. Quindi il tipo di throwespressione non sembra essere nullo.

Quindi, cos'è?

Se rispondi, esegui il backup delle tue affermazioni con citazioni dallo Standard.


Si è scoperto che non riguardava tanto il tipo di espressione di lancio quanto il modo in cui l'operatore condizionale gestisce le espressioni di lancio - qualcosa di cui certamente non sapevo prima di oggi. Grazie a tutti coloro che hanno risposto, ma in particolare a David Thornley.

c++  throw 

10
+1 Domanda fantastica. E un modo intelligente per testarlo.
Jeremy Powell

1
Quel collegamento sembra rendere abbastanza chiaro che il tipo è determinato dal compilatore per essere quello che deve essere.
Draemon

L'articolo collegato credo sia stato aggiornato da quando l'ho guardato, e sono sicuro che sia effettivamente così. Tuttavia, non riesco a trovarlo nello standard.

Ae forse no - doppia d = lancia "pippo"; è un errore con g + = (non l'ho testato con comeau)

+1 Sono curioso di conoscere la risposta.
AraK

Risposte:


96

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

Infatti, 15.1, paragrafo 1 dice esplicitamente "Un'espressione di lancio è di tipo void."


9
OK, penso che abbiamo un vincitore.

Nota che le espressioni di lancio sono espressioni 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 (il primo argomento dell'operatore incorporato, ad esempio), è un errore di tipo.
AProgrammer

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

31

"Un'espressione di lancio è di tipo void"

ISO14882 Sezione 15


Quindi sia g ++ che Comeau sono negligenti nel non dare un errore per il mio // 1 caso?

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

13

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

Se il secondo o il terzo operando ha il tipo (possibilmente cv-quali fi cato) void, le conversioni standard lvalue-to-rvalue, array-to-pointer e function-to-pointer vengono eseguite sul secondo e terzo operando, e uno dei seguenti deve contenere:

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

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

Quindi, con //1te eri nel primo caso, con //2, stavi violando "uno dei seguenti si terrà", poiché nessuno di loro lo fa, in quel caso.


3

Puoi fare in modo che una stampante di caratteri lo sputi per te :

template<typename T>
struct PrintType;

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

Fondamentalmente la mancanza di implementazione per PrintTypefarà sì che il rapporto di errore di compilazione dica:

istanziazione implicita di un modello non definito PrintType<void>

quindi possiamo effettivamente verificare che le throwespressioni siano 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)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.