2009-07-31 4 views
113

Wybrałem to w jednym z moich krótkich wypadów do reddit:W C++, jeśli rzut jest wyrażeniem, jaki jest jego typ?

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 wyrazem. Jest to dość jasno określone w standardzie C++, zarówno w tekście głównym, jak i gramatyce. Jednak nie jest jasne (przynajmniej dla mnie), jaki jest typ tego wyrażenia? Domyślałem „void”, ale trochę eksperymentować z g ++ 4.4.0 i Comeau dały 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łem problemu z // 1 // ale barfed na 2 ponieważ typów w Operator warunkowy jest inny. Tak więc typ wyrażenia throw nie wydaje się być nieważny.

Co to jest?

Jeśli odpowiesz, proszę wykonać kopię zapasową wyciągów za pomocą cytatów ze standardu.


To okazało się nie być tak dużo o rodzaju wyrażenia rzutów jak jak warunkowe oferty operator z wyrażeń rzutów - coś, co na pewno nie wiedzieli o przed dzisiaj. Dziękuję wszystkim, którzy odpowiedzieli, ale szczególnie Davidowi Thornleyowi.

+10

+1 Niesamowite pytanie. I sprytny sposób na testowanie tego. –

+1

To łącze wydaje się dość jasne, że typ jest określany przez kompilator, aby być czymkolwiek, czym musi być. – Draemon

+0

Linkowany artykuł został chyba zaktualizowany, odkąd go obejrzałem, i jestem pewien, że tak właśnie jest. Jednak nie mogę go znaleźć w standardzie. –

Odpowiedz

94

Zgodnie z normą, 5,16 ust 2 pierwszy punkt „, drugiego lub trzeciego argumentu (ale nie oba) jest wyrażeniem rzutowania (15.1), wynikiem jest typ drugiego i jest wartością rownową. " W związku z tym operator warunkowy nie dba o to, jaki typ jest wyrażeniem throw, ale po prostu użyje innego typu.

W rzeczywistości 15.1, paragraf 1 mówi wprost "Wyrażenie rzutu jest nieważne".

+9

OK - Myślę, że mamy zwycięzcę. –

+0

Należy zauważyć, że wyrażenie throw jest wyrażeniem przypisania. Więc są one 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ą one ignorowane (na przykład pierwszy argument wbudowanego operatora), jest to błąd typu. – AProgrammer

+4

Co naprawdę mnie zaskakuje, to że pomyśleli o tej sprawie i zrobili coś rozsądnego. – Omnifarious

31

"Rzut ekspresja jest typu void"

ISO14882 sekcji 15

+0

Zatem zarówno g ++, jak i Comeau, nie dają błędu w przypadku mojego // 1? –

+2

@Neil, nie tak naprawdę, ponieważ zgodnie z C++/5.16/2, drugi i trzeci operand operatora warunkowego może być typu 'void' – mloskot

13

Z [expr.cond.2] (operator warunkowego ?:)

przypadku albo drugi albo trzeci argument operacji jest typu (ewentualnie CV Quali ed fi) próżnia, a następnie lwartością do RValue, tablica do wskaźnika i funkcji typu na wskaźnik standardowe konwersje są wykonane na drugiej i operandów trzecie i jedną z następujących dysponuje:

- druga lub trzecia operandu (ale nie obydwa) jest wyrażenie rzutowe; 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 rzutowania.- koniec uwaga]

Tak, z //1 byłaś w pierwszym przypadku, z //2, zostałeś naruszające „jedną z poniższych czynności trzymaj”, ponieważ żaden z nich zrobić w tej sprawie.

3

Można mieć drukarkę typu spit it out for you:

template<typename T> 
struct PrintType; 

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

Zasadniczo brak wdrożenia do PrintType spowoduje raport o błędach kompilacji powiedzieć:

niejawna instancji o nieokreślonej szablonu PrintType<void>

dzięki czemu możemy faktycznie zweryfikować, że throw wyrażeń jest typu void (i tak, standardowe cytaty wymienione w innych odpowiedziach potwierdzają, że nie jest to wynik specyficzny dla implementacji - chociaż gcc ma problemy z wydrukowaniem cennych informacji)