W C ++, jeśli throw jest wyrażeniem, jaki jest jego typ?

115

Podniosłem to podczas jednej z moich krótkich wypraw na reddit:

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

Zasadniczo autor zwraca uwagę, że w C ++:

throw "error"

jest wyrażeniem. W rzeczywistości jest to dość jasno opisane w standardzie C ++, zarówno w tekście głównym, jak i gramatyce. Jednak nie jest jasne (przynajmniej dla mnie), jaki jest typ wyrażenia? Zgadłem „ void”, ale trochę poeksperymentowałem z g ++ 4.4.0 i Comeau dostarczyło ten kod:

    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
    }

Kompilatory nie miały problemu z // 1, ale barfed na // 2, ponieważ typy w operatorze warunkowym są różne. Tak więc rodzaj throwwyrażenia nie wydaje się być nieważny.

Więc co to jest?

Jeśli odpowiesz, poprzyj swoje wypowiedzi cytatami ze Standardu.


Okazało się, że nie tyle chodziło o typ wyrażenia rzucającego, ile o to, jak operator warunkowy radzi sobie z wyrażeniami rzucania - coś, o czym z pewnością nie wiedziałem wcześniej. Dziękuję wszystkim, którzy odpowiedzieli, ale szczególnie Davidowi Thornleyowi.


źródło
10
+1 Fantastyczne pytanie. I sprytny sposób na przetestowanie tego.
Jeremy Powell
1
To łącze wydaje się dość jasne, że kompilator określa, jaki typ ma być.
Draemon
Wydaje mi się, że powiązany artykuł został zaktualizowany, odkąd go obejrzałem, i jestem pewien, że tak jest. Jednak nie mogę znaleźć tego w standardzie.
A może nie - double d = throw "foo"; jest błędem z g + = (nie testowałem go z comeau)
+1 Jestem ciekawy odpowiedzi.
ARAK

Odpowiedzi:

96

Zgodnie ze standardem, punkt 5.16, paragraf 2, pierwszy punkt, „Drugi lub trzeci operand (ale nie oba) jest wyrażeniem rzutowym (15.1); wynik jest typu drugiego i jest wartością r”. W związku z tym operator warunkowy nie dba o typ wyrażenia rzucającego, ale po prostu użyje innego typu.

W rzeczywistości, punkt 15.1, akapit 1 mówi wyraźnie: „Wyrażenie rzucające jest typu void”.

David Thornley
źródło
9
OK - myślę, że mamy zwycięzcę.
Zwróć uwagę, że wyrażenie rzucające jest wyrażeniem przypisania. Są więc błędem składni jako argumentem dla większości operatorów. Oczywiście można je ukryć w nawiasach, ale jeśli nie są ignorowane (na przykład pierwszy argument operatora wbudowanego), jest to błąd typu.
AProgrammer
4
Najbardziej zaskakuje mnie to, że pomyśleli o tej sprawie i sprawili, że wydarzyło się coś rozsądnego.
Omnifarious
31

„Wyrażenie wrzutu jest typu void”

ISO14882 sekcja 15

Draemon
źródło
Zatem zarówno g ++, jak i Comeau nie podają błędu w moim przypadku // 1?
2
@Neil, niezupełnie, ponieważ zgodnie z C ++ / 5.16 / 2, drugi i trzeci void
operand
13

Z [wyraż.cond.2] (operator warunkowy ?:):

Jeśli drugi lub trzeci operand ma typ (prawdopodobnie z kwalifikacją cv) void, wówczas konwersje l-wartość-na-wartość, tablica-wskaźnik i standardowa konwersja funkcji na wskaźnik są wykonywane na drugim i trzecim operandzie i jeden z poniższych musi posiadać:

- Drugi lub trzeci operand (ale nie oba) jest wyrażeniem rzutowym; wynik jest typu drugiego i jest wartością r.

- Zarówno drugi, jak i trzeci operand mają typ void; wynik jest typu void i jest wartością r. [Uwaga: obejmuje to przypadek, w którym oba operandy są wyrażeniami rzucanymi. - notatka końcowa]

Tak więc, z //1tobą byłeś w pierwszym przypadku z //2, naruszałeś zasadę „jedno z poniższych będzie utrzymywać”, ponieważ żaden z nich tego nie robi w tym przypadku.

Marc Mutz - mmutz
źródło
3

Możesz poprosić drukarkę typu wypluć to dla ciebie :

template<typename T>
struct PrintType;

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

Zasadniczo brak implementacji dla PrintTypespowoduje, że raport o błędzie kompilacji będzie zawierał:

niejawna instancja niezdefiniowanego szablonu PrintType<void>

więc możemy faktycznie zweryfikować, czy throwwyrażenia są typu void(i tak, standardowe cytaty wspomniane w innych odpowiedziach potwierdzają, że nie jest to wynik specyficzny dla implementacji - chociaż gcc ma trudności z wydrukowaniem cennych informacji)

Nikos Athanasiou
źródło