Сложная синтаксическая конструкция с собеседования

auto fry()try{}catch(int){try{}catch(...){}}catch(int){}

Объясните, что означает эта конструкция. Почему некоторые компиляторы компилируют без ошибок (gcc), а некоторые - с (msvc)?. Как правильно с точки зрения стандарта?


Ответы (1 шт):

Автор решения: gbg

Ну давайте сделаем разбор данной бубылды.

Первым делом, мы приведем это в читаемый вид (как престарелый квакер, я воспользуюсь скобками Кармака):

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, но с тем же смыслом - негоже делать два обработчика с одинаковым (или приводимым) типом - работать будет только верхний.

В блоке обработки исключения есть еще вложенный блок, но так как там нет кода, ничего примечательного про него в данный момент сказать нельзя.

→ Ссылка