Должен ли я объявлять конструктор копирования за исключениями?
В более эффективном С++ Скотт Мейерс говорит
С++ указывает, что копируется объект, созданный как исключение.
Я полагаю, что если конструктор копирования генерирует исключение по очереди, вызывается std::terminate
, поэтому это хорошая причина для объявления всех конструкторов копирования моих исключений noexcept
(а также, я думаю, не бросать объекты, которые выделяют память из кучи, например std::string
).
Тем не менее я был удивлен, увидев, что стандартная реализация библиотеки, поставляемая с GCC 4.7.1, не определяет эти конструкторы копирования для std::bad_alloc
и std::exception
. Не должны ли они определять их noexcept
?
Ответы
Ответ 1
Раздел 18.8.1 [exception]/p1 указывает:
namespace std {
class exception {
public:
exception() noexcept;
exception(const exception&) noexcept;
exception& operator=(const exception&) noexcept;
virtual ~exception();
virtual const char* what() const noexcept;
};
}
т.е. конструктор копирования и присваивание копии std:: exception должны быть noexcept
, и это можно проверить с помощью:
static_assert(std::is_nothrow_copy_constructible<std::exception>::value, "");
static_assert(std::is_nothrow_copy_assignable<std::exception>::value, "");
т.е. если реализация не делает эти элементы незаметными, то в этом отношении это не соответствует.
Аналогично, 18.6.2.1 [bad.alloc]/p1 также указывает noexcept copy:
namespace std {
class bad_alloc : public exception {
public:
bad_alloc() noexcept;
bad_alloc(const bad_alloc&) noexcept;
bad_alloc& operator=(const bad_alloc&) noexcept;
virtual const char* what() const noexcept;
};
}
Кроме того, все для типов исключений, определенных в std, не имеют каких-либо элементов экземпляра, явно или неявно. Для типов, определенных в <stdexcept>
, это обычно выполняется с буфером с подсчетом ссылок для строки what()
. Это поясняется в [exception]/p2:
Каждый стандартный класс библиотеки T
, который выводится из исключения класса должен иметь публично доступный конструктор копий и публично доступный оператор назначения копирования, который не выходит с исключение....
То есть, в качественной реализации (и в этом отношении не требуется героика для создания качественной реализации), не только экземпляры экземпляров исключений исключают исключение (естественно, потому что они отмечены noexcept
), они также не будут называть terminate()
.
Нет режима сбоя при копировании std-определенных типов исключений. Либо нет данных для копирования, либо данные подсчитываются и неизменяемы.
Ответ 2
Распределение памяти для исключений выполняется за пределами обычных каналов:
15.1 Бросок исключения [except.throw]
3 Выбрасывание копии исключения - инициализирует (8.5, 12.8) временную объект, называемый объектом исключения. [...]
4 Память для объекта исключения выделено неуказанным способом, за исключением случаев, указанных в 3.7.4.1. [...]
3.7.4.1 Функции распределения [basic.stc.dynamic.allocation]
4 Глобальная функция распределения называется только как результат новой выражение (5.3.4) или вызывается непосредственно с помощью синтаксиса вызова функции (5.2.2) или косвенно вызван вызовами функций в Стандартная библиотека С++. [Примечание: в частности, глобальное распределение функция не вызывается для выделения хранилища для [...] для объекта исключения (15.1). -end note]
В большинстве реализаций имеется отдельная область памяти, из которой выделяются объекты исключений, так что даже если вы повторно бросаете объект исключения std::bad_alloc
, самому измученному свободному хранилищу не предлагается выделить скопированный объект исключения. Таким образом, не должно быть причины для копирования, чтобы генерировать другое исключение.
Ответ 3
Ну, все хорошо и хорошо объявить его noexcept
, но это требует, чтобы вы могли ГАРАНТИРОВАТЬ, чтобы он не выдавал исключение (и для портативного кода, во всех его реализациях!). Я ожидаю, что это причина того, что стандартные не объявлены таким образом.
Очевидно, нет никакого вреда в объявлении конструктора копирования noexcept
, но это может быть довольно ограничительным, чтобы попытаться достичь этого.