Сложная синтаксическая конструкция с собеседования
auto fry()try{}catch(int){try{}catch(...){}}catch(int){}
Объясните, что означает эта конструкция. Почему некоторые компиляторы компилируют без ошибок (gcc), а некоторые - с (msvc)?. Как правильно с точки зрения стандарта?
Ответы (1 шт):
Ну давайте сделаем разбор данной бубылды.
Первым делом, мы приведем это в читаемый вид (как престарелый квакер, я воспользуюсь скобками Кармака):
auto fry()
try
{
}
catch(int)
{
try
{
}
catch(...)
{
}
}
catch(int)
{
}
Нулевая причина посчитать это некорректным - у функции нету тела (Что? Да!) - смотрите, try идет вне блока кода, сразу за круглой скобкой, которой заканчивается список параметров. Но это всего лишь function-try-block, EN.SO ничего страшного, так тоже писать можно.
Тут мы объявили функцию, которая, казалось бы, может возвращать что угодно (потому что тип стоит auto, что в данном случае сомнительно - шаблонов нет, паравозов из неймспейсов нет - почему бы не сократить пространство для ошибок. Более того, C++11 и ниже это не примет, потому что auto для возврата из функций завезли только в C++14). Фактичкески функция ничего не делает (как верно заметил в комментариях @Harry), поэтому на деле, она будет возвращать void.
Дальше у нас идет развесистая гирлянда из обработчиков исключений, которые никогда не полетят - просто потому что код в блоке try отсутствует.
Обработчики исключений обрабатываются сверху-вниз и в соответствии с возможностью преобразования типов (cppreference, TODO: выкопать параграфы в Стандарте). Вот тут очень важен союз И, потому что наличие двух обработчиков с одинаковой сигнатурой (или с возможностью приведения типов) стандарт допускает, но тот который ниже - никогда не вызовется:
try
{
f();
}
catch (const std::exception& e)
{
// will be executed if f() throws std::runtime_error
}
catch (const std::runtime_error& e)
{
// dead code!
}
Собственно, по этой причине, GCC этот код сожрал.
MSVC же считает, что это повод выругаться ошибкой C2312, причем пример, который они там приводят, по структуре и форме совпадает примером выше:
// C2312.cpp
// compile with: /EHsc
#include <eh.h>
int main()
{
try
{
throw "ooops!";
}
catch( signed int ) {}
catch( int ) {} // C2312
}
С ними трудно спорить - раз такая оказия произошла, вероятно, это ошибка, человеческий фактор (перепутаны местами блоки или типы), вот и ругаемся.
clang же ограничивается выдачей Warning, но с тем же смыслом - негоже делать два обработчика с одинаковым (или приводимым) типом - работать будет только верхний.
В блоке обработки исключения есть еще вложенный блок, но так как там нет кода, ничего примечательного про него в данный момент сказать нельзя.