Можно ли объявить функцию main с помощью спецификатора noexcept?

Допустим ли следующий код в C++?

int main() noexcept
{
}

И clang++ 3.8.0, и g++ 7.2.0 компилировать нормально-std=c++14 -O0 -Wall -Wextra -Werror -pedantic-errors флагами компиляции).

Можно ли использовать сложные условия (например, включая оператор noexcept) в спецификации noexcept функции main?

А как же С++17? Насколько я знаю, спецификатор noexcept становится частью типа функции в этой версии стандарта.


person Constructor    schedule 30.11.2017    source источник
comment
Я не могу найти ничего о основной функции, касающейся noexcept в Стандарте, подразумевая, что это разрешено, но опять же я пьян, так что, надеюсь, кто-нибудь присоединится.   -  person DeiDei    schedule 01.12.2017
comment
Что именно имело бы значение? Если исключение выходит за пределы main, необходимо вызвать std::terminate. Если исключение выходит из функции noexcept, необходимо вызвать std::terminate. Для всех полезных целей main уже есть noexcept. И поскольку вы не можете ни вызвать main, ни получить на него указатель... какая разница?   -  person Nicol Bolas    schedule 01.12.2017
comment
@NicolBolas Интересное замечание. Я думал, что смогу найти пример кода, где выбрасывание int main() и int main() noexcept дает разные результаты, но я не могу сделать это быстро. Возможно, вы правы, и такого примера вообще нет.   -  person Constructor    schedule 01.12.2017
comment
@Constructor: есть одно отличие. Если вы попытаетесь бросить через функцию noexcept, раскрутка стека никогда не произойдет. В то время как, если вы попытаетесь пробросить через main, может произойти раскручивание стека; это зависит от реализации.   -  person Nicol Bolas    schedule 01.12.2017
comment
@NicolBolas Это странно, но clang++ показывает прямо противоположное поведение: это выглядит как с noexcept выполняется раскручивание стека, а без noexcept нет .   -  person Constructor    schedule 01.12.2017
comment
@Constructor Это больше похоже на ошибку, чем на что-либо еще.   -  person Passer By    schedule 01.12.2017
comment
@NicolBolas Нет, в обоих случаях раскручивание определяется реализацией.   -  person T.C.    schedule 01.12.2017
comment
@TC: Где это сказано в стандарте? Разматывание не происходит до тех пор, пока не будет найден обработчик. Если noexcept встречается до того, как был найден обработчик, то стандарт говорит, что вызывается std::terminate. Я не вижу, где это позволяет реализации вызывать раскручивание до функции noexcept.   -  person Nicol Bolas    schedule 01.12.2017
comment
@NicolBolas [кроме.terminate]/2.   -  person T.C.    schedule 01.12.2017


Ответы (2)


Стандарт [[basic.start.main]] определяет следующие ограничения для функции main:

Реализация должна позволять:

— функция(), возвращающая int и

— функция (int, указатель на указатель на char), возвращающая int

Более того:

Программа, определяющая main как удаленную или объявляющая main встроенной, статической или constexpr, является некорректной.

На практике спецификаций квалификатора noexcept для main не существует. С другой стороны, noexcept можно использовать в качестве спецификатора для любой функции. Это означало бы, что main noexcept не неправильный формат.


Какая разница без noexcept main?

Поскольку стандарт не очень подробно описывает функцию noexcept для main, как мы видели, мы можем попытаться вывести некоторое поведение и проверить реализации.

Из здесь:

Всякий раз, когда генерируется исключение и поиск обработчика обнаруживает самый внешний блок функции, не вызывающей исключение, вызывается функция std::terminate.

Общее правило для исключений можно найти здесь:

Если возникает и не перехватывается исключение, включая исключения, которые обходят начальную функцию std::thread, основную функцию и конструктор или деструктор любых статических или локальных объектов потока, то вызывается std::terminate. Реализация определяет, происходит ли раскручивание стека для неперехваченных исключений.

Это означает, что функция throw из main всегда генерирует вызов std::terminate. Независимо от noexcept спецификации main.

Сторона реализации:

Действительно, следующие коды:

int main(int argc, char* argvp[]) {
  throw 1;
  return 0;
}

а также

int main(int argc, char* argvp[]) noexcept {
  throw 1;
  return 0;
}

будет производить ту же выходную сборку. Например, в GCC:

main:
        movl    $4, %edi
        subq    $8, %rsp
        call    __cxa_allocate_exception
        xorl    %edx, %edx
        movl    $1, (%rax)
        movl    typeinfo for int, %esi
        movq    %rax, %rdi
        call    __cxa_throw

Это означает, что он будет преобразован в вызов std::terminate, потому что кадр стека пуст на основном уровне, независимо от спецификации noexcept.

person BiagioF    schedule 30.11.2017
comment
Как насчет разницы между int main() noexcept и простым int main()? - person Constructor; 01.12.2017
comment
@Constructor Я пытался вывести разницу в окончательном ответе. - person BiagioF; 01.12.2017

Тип main в int main() noexcept; — это «функция (), возвращающая int» в C++14 и «noexcept функция (), возвращающая int» в C++17.

Первый явно требуется для поддержки [basic.start.main]. Последнее не так.

Это похоже на дефект в C++17.

person T.C.    schedule 01.12.2017
comment
Кто-то, кто с большим энтузиазмом относится к сообщениям об основных проблемах, чем я, должен связаться с Майком Миллером из EDG, председателем CWG, чтобы — при условии, что о ней не сообщалось ранее — эта проблема появилась в списке основных проблем через два года. - person T.C.; 01.12.2017
comment
проблема появится в списке основных проблем через два года Так что оптимистично… :D - person Language Lawyer; 06.01.2020