В C ++, если throw является выражением, каков его тип?

115

Я заметил это в одном из своих кратких набегов на Reddit:

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

В основном автор указывает, что в C ++:

throw "error"

это выражение. На самом деле это довольно четко прописано в стандарте C ++ как в основном тексте, так и в грамматике. Однако не совсем ясно (по крайней мере, мне), что это за тип выражения? Я догадался " void", но немного поэкспериментировав с g ++ 4.4.0 и Comeau, дал следующий код:

    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
    }

У компиляторов не было проблем с // 1, но они отказались от // 2, потому что типы в условном операторе разные. Итак, типthrow выражения не кажется недействительным.

Так что это?

Если вы ответите, пожалуйста, подкрепите свои утверждения цитатами из Стандарта.


Оказалось, что дело не столько в типе выражения throw, сколько в том, как условный оператор работает с выражениями throw - о чем я определенно не знал до сегодняшнего дня. Спасибо всем, кто ответил, но особенно Дэвиду Торнли.


источник
10
+1 Классный вопрос. И умный способ проверить это.
Джереми Пауэлл
1
Эта ссылка, кажется, довольно ясно дает понять, что тип определяется компилятором как то, что ему нужно.
Draemon
Связанная статья, я думаю, была обновлена ​​с тех пор, как я ее просмотрел, и я уверен, что это действительно так. Однако я не могу найти его в стандарте.
А, а может и нет - double d = throw "foo"; ошибка с G + = (не протестировали его с Комео)
+1 Интересно узнать ответ.
AraK

Ответы:

96

Согласно стандарту, п. 5.16 параграф 2, первая точка: «Второй или третий операнд (но не оба сразу) являются выражением throw (15.1); результат имеет тип другого и является r-значением». Следовательно, условный оператор не заботится о типе выражения throw, он просто использует другой тип.

Фактически, в параграфе 1 15.1 прямо сказано: «Выражение-бросок имеет тип void».

Дэвид Торнли
источник
9
Хорошо - я думаю, у нас есть победитель.
Обратите внимание, что выражение-бросок является выражением-присваиванием. Таким образом, они являются синтаксической ошибкой в ​​качестве аргумента для большинства операторов. Очевидно, вы можете скрыть их в скобках, но если они не игнорируются (например, первый аргумент встроенного оператора), это ошибка типа.
AProgrammer
4
Что меня действительно удивляет, так это то, что они подумали об этом деле и сделали что-то разумное.
Omnifarious
31

"Выражение-бросок имеет тип void"

ISO14882 Раздел 15

Draemon
источник
Значит, и g ++, и Comeau упускают из виду, что не выдали ошибку для моего // 1 случая?
2
@Neil, не совсем так, потому что согласно C ++ / 5.16 / 2, второй и третий операнды условного оператора могут быть типаvoid
mloskot
13

Из [expr.cond.2] (условный оператор ?:):

Если второй или третий операнд имеет тип (возможно с квалификацией cv) void, то стандартные преобразования lvalue-to-rvalue, array-to-pointer и function-to-pointer выполняются для второго и третьего операндов, и одно из следующих должно выполняться:

- Второй или третий операнд (но не оба) являются выражением throw; результат относится к другому типу и является r-значением.

- И второй, и третий операнды имеют тип void; результат имеет тип void и является rvalue. [Примечание: это включает случай, когда оба операнда являются выражениями типа throw. - конец примечания]

Итак, //1вы были в первом случае, //2вы нарушили «одно из следующих должно выполняться», поскольку в этом случае ни один из них не нарушает.

Марк Мутц - mmutz
источник
3

Вы можете попросить типографский принтер выплюнуть это за вас :

template<typename T>
struct PrintType;

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

В основном отсутствие реализации PrintTypeприведет к тому, что отчет об ошибке компиляции будет говорить:

неявное создание неопределенного шаблона PrintType<void>

поэтому мы действительно можем проверить, что throwвыражения относятся к типу void(и да, стандартные кавычки, упомянутые в других ответах, подтверждают, что это не результат конкретной реализации, хотя gcc с трудом выводит ценную информацию)

Никос Афанасиу
источник