Wenn in C ++ throw ein Ausdruck ist, welchen Typ hat er?

115

Ich habe dies in einem meiner kurzen Streifzüge zum Reddit aufgegriffen:

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

Grundsätzlich weist der Autor darauf hin, dass in C ++:

throw "error"

ist ein Ausdruck. Dies ist im C ++ - Standard sowohl im Haupttext als auch in der Grammatik ziemlich klar formuliert. Was jedoch (zumindest für mich) nicht klar ist, ist die Art des Ausdrucks? Ich vermutete " void", aber ein bisschen Experimentieren mit g ++ 4.4.0 und Comeau ergab diesen Code:

    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
    }

Die Compiler hatten kein Problem mit // 1, aber mit // 2 gesperrt, da die Typen im bedingten Operator unterschiedlich sind. Die Art eines throwAusdrucks scheint also nicht ungültig zu sein.

Also, was ist es?

Wenn Sie antworten, stützen Sie Ihre Aussagen bitte mit Zitaten aus dem Standard.


Es stellte sich heraus, dass es nicht so sehr um die Art eines Wurfausdrucks ging, sondern vielmehr darum, wie der bedingte Operator mit Wurfausdrücken umgeht - etwas, von dem ich bis heute sicherlich nichts wusste. Vielen Dank an alle, die geantwortet haben, besonders aber an David Thornley.


quelle
10
+1 Tolle Frage. Und eine clevere Art, es zu testen.
Jeremy Powell
1
Dieser Link scheint ziemlich deutlich zu machen, dass der Typ vom Compiler als das bestimmt wird, was er sein muss.
Draemon
Der verlinkte Artikel wurde meiner Meinung nach aktualisiert, seit ich ihn mir angesehen habe, und ich bin mir sicher, dass dies tatsächlich der Fall ist. Ich kann es jedoch nicht im Standard finden.
Und vielleicht auch nicht - double d = "foo" werfen; ist ein Fehler mit g + = (habe es nicht mit comeau getestet)
+1 Ich bin gespannt auf die Antwort.
AraK

Antworten:

96

Gemäß dem Standard, 5.16 Absatz 2, erster Punkt: "Der zweite oder dritte Operand (aber nicht beide) ist ein Wurfausdruck (15.1); das Ergebnis ist vom Typ des anderen und ist ein Wert." Daher ist es dem bedingten Operator egal, welcher Typ ein Wurfausdruck ist, sondern er verwendet nur den anderen Typ.

Tatsächlich heißt es in Absatz 1, 15.1, ausdrücklich: "Ein Wurfausdruck ist vom Typ void."

David Thornley
quelle
9
OK - ich denke wir haben einen Gewinner.
Beachten Sie, dass Wurfausdrücke Zuweisungsausdrücke sind. Daher sind sie für die meisten Operatoren ein Syntaxfehler als Argument. Natürlich können Sie sie in Klammern ausblenden, aber wenn sie nicht ignoriert werden (z. B. erstes Argument des eingebauten Operators), handelt es sich um einen Typfehler.
AProgrammer
4
Was mich wirklich überrascht, ist, dass sie an diesen Fall gedacht und etwas Vernünftiges geschehen lassen.
Omnifarious
31

"Ein Wurfausdruck ist vom Typ void"

ISO14882 Abschnitt 15

Draemon
quelle
Dann geben sowohl g ++ als auch Comeau keinen Fehler für meinen // 1 Fall?
2
@Neil, nicht wirklich, weil gemäß C ++ / 5.16 / 2 der zweite und dritte Operand des bedingten Operators vom Typvoid
mloskot
13

Aus [Ausdruck.2] (bedingter Operator ?:):

Wenn entweder der zweite oder der dritte Operand vom Typ (möglicherweise cv-qualifiziert) void ist, werden die Standardkonvertierungen von lWert zu rWert, Array zu Zeiger und Funktion zu Zeiger für den zweiten und dritten Operanden ausgeführt, und eine der folgenden gilt:

- Der zweite oder dritte Operand (aber nicht beide) ist ein Wurfausdruck; Das Ergebnis ist vom Typ des anderen und ist ein Wert.

- Sowohl der zweite als auch der dritte Operand haben den Typ void; Das Ergebnis ist vom Typ void und ein r-Wert. [Hinweis: Dies schließt den Fall ein, in dem beide Operanden Wurfausdrücke sind. - Endnote]

Wenn //1Sie also im ersten Fall mit waren //2, haben Sie gegen "eine der folgenden Bedingungen" verstoßen, da in diesem Fall keiner von ihnen dies tut.

Marc Mutz - mmutz
quelle
3

Sie können einen Typdrucker haben ausspucken lassen :

template<typename T>
struct PrintType;

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

Grundsätzlich führt die fehlende Implementierung für PrintTypedazu, dass im Kompilierungsfehlerbericht Folgendes angegeben wird:

implizite Instanziierung einer undefinierten Vorlage PrintType<void>

So können wir tatsächlich überprüfen, ob throwAusdrücke vom Typ sind void(und ja, die in anderen Antworten erwähnten Standardzitate bestätigen, dass dies kein implementierungsspezifisches Ergebnis ist - obwohl es für gcc schwierig ist, wertvolle Informationen zu drucken).

Nikos Athanasiou
quelle