Ответ 1
Другими словами, какое из перечисленных выше включает в себя
noexcept
noexcept-specification...?
Спецификация исключения (noexcept
и динамическая спецификация исключений) относится к построению базовых классов, построению и инициализации членов и коду в теле конструктора. В принципе, все функции, выполняемые при построении объекта, - это имеет смысл в том, что спецификация исключения привязана к конструктору объекта, поэтому она должна охватывать код, выполняемый во время построения объекта; это было бы противоречиво, если бы какая-либо часть конструкции не была покрыта этим.
Поддержка стандартных котировок...
Что делать, если при построении (и, возможно, не обрабатывается) исключение возникает?
Всякий раз, когда создается исключение типа
E
, и поиск обработчика ([except.handle]) встречает самый внешний блок функции с спецификацией исключения, которая не позволяетE
, тогда
- Если определение функции имеет спецификацию динамического исключения, вызывается функция
std::unexpected()
([except.unexpected]),- в противном случае вызывается функция
std::terminate()
([except.terminate]).
Что означает "внешний блок функции"? Тело функции. 1
спецификация исключений выше содержит спецификацию noexcept.
Как определяется неявно объявленная спецификация исключений для неявно объявленного конструктора?
Неявно объявленная специальная функция-член
f
некоторого классаX
считается неявной спецификацией исключения, которая состоит из всех членов из следующих наборов:
если
f
- конструктор,
множества потенциальных исключений вызовов конструктора
- для
X
невариантных нестатических членов данных,- для
X
прямых базовых классов иесли
X
не является абстрактным ([class.abstract]), дляX
виртуальных базовых классов(включая выражения аргументов по умолчанию, используемые в таких вызовах), выбранных с помощью разрешения перегрузки для неявного определения f ([class.ctor])...
наборы возможных исключений инициализации нестатических элементов данных из инициализаторов скобок или равных, которые не игнорируются ([class.base.init]);
Это дает очень полезное разъяснение того, что компилятор будет использовать для определения (и, следовательно, рассмотренного) спецификации исключения.
1 Что означает "внешний блок функции"? Было высказано замечание относительно озабоченности определением блока функции. Стандарт не имеет формального определения блока функции. Блок фразы функции используется только в Обработка исключений [кроме]. Фраза была включена в стандарт еще с С++ 98.
Для большей ясности по этому вопросу нам потребуется искать альтернативный источник и делать некоторые разумные выводы.
тело функции - самый внешний блок функции. См. Также: try-block, определение функции. TС++ PL 2.7, 13.
И из [dcl.fct.def.general]/1 грамматика для тела функции, которая покрывает ctor-инициализатор соединением -statement и функция-try-block;
Определения функций имеют вид:
...
функция тела:
ctor-initializer opt составная заявка
функция попробуйте-блок...
Любая неофициальная ссылка на тело функции должна интерпретироваться как ссылка на нетерминальное тело функции...
Также важно иметь в виду, что спецификации исключений связаны с функциями, а не с общими блоками кода (областями с ограничениями и т.д.).
Учитывая возраст фразы в предложении обработки исключений и часто задаваемые вопросы Stroustrup, блок функции совпадает с функциональным телом, этот стандарт, вероятно, может быть связан с обновлением языка, используемого в предложении исключения.
Некоторые эмпирические данные, приведенные ниже, для построения a1
, a2
и a3
(когда остальные комментируются), вызывают вызов std::terminate
. Результат применим для g++, clang и MSVC.
struct Thrower { Thrower() { std::cout << "throwing..." << std::endl; throw 42; } };
struct AsMember { Thrower t_; AsMember() noexcept : t_{} { std::cout << "ctor" << std::endl; } };
struct AsBase : Thrower { AsBase() noexcept { std::cout << "ctor" << std::endl; } };
struct AsNSDMI { Thrower t_ {}; AsNSDMI() noexcept { std::cout << "ctor" << std::endl; } };
int main()
{
std::set_terminate([](){ std::cout << "terminating..." << std::endl; });
try {
//AsMember a1{};
//AsBase a2{};
AsNSDMI a3{};
}
catch (...) { std::cout << "caught..." << std::endl; }
return 0;
}