Скопировать исключение возвращаемых значений и noexcept
У меня есть такой шаблон функции:
template <typename T>
constexpr auto myfunc() noexcept
{
return T{};
}
Этот шаблон функции гарантированно не будет исключением из-за исключения копирования? Если исключение выдается внутри конструктора, происходит ли это внутри или вне функции?
3 ответа
Все, что делает elision copy, - это исключает фактическую копию или перемещение. Все происходит так, как если бы все происходило без исключения копирования (конечно, за исключением самой копии).
Конструкция происходит внутри функции. Копия elision не меняет этого. Все, что он делает - это исключает фактическое копирование / перемещение (я повторяюсь?) В результате возврата возвращаемого значения функции обратно в вызывающую функцию.
Итак, если конструктор класса по умолчанию выдает исключение, noexcept
уничтожить все это с высокой орбиты.
Если конструктор копирования / перемещения выдает исключение, поскольку копирование / перемещение не происходит, все продолжается.
С gcc 7.3.1, скомпилированным с использованием -std= C++17:
template <typename T>
constexpr auto myfunc() noexcept
{
return T{};
}
class xx {
public:
xx() { throw "Foo"; }
};
int main()
{
try {
myfunc<xx>();
} catch (...) {
}
}
Результат:
terminate called after throwing an instance of 'char const*'
Теперь давайте смешаем это и сгенерируем исключение в конструкторах копирования и перемещения:
class xx {
public:
xx() { }
xx(xx &&) { throw "Foo"; }
xx(const xx &) { throw "Baz"; }
};
Это работает без исключения.
Инициализация возвращаемого значения происходит в контексте вызываемого объекта (функции, содержащей оператор). То есть, если вы хотите оставить открытой возможность обработки исключения, выбрасываемого конструктором по умолчанию, вам не следует объявлять
Я понимаю источник путаницы: согласно таксономии категорий значений в С++17 и более поздних версиях, prvalue — это рецепт создания объекта, а не сам объект. Рассмотрим следующий код:
T foo() {
return {};
}
T t = foo();
В С++ 14 оператор и инициализация — это два отдельных шага, хотя исключение разрешено в качестве оптимизации. На первом этапе возвращаемый объект ( он же "") инициализируется копированием из
Таким образом, вы можете подумать, что в C++17 происходит аналогичный двухэтапный процесс, только с пересмотренной концепцией prvalue: а именно, поскольку
Однако в стандарте есть явный язык, который отрицает такую интерпретацию:
оператор return инициализирует объект результата glvalue или prvalue вызова функции (явного или неявного) путем инициализации копирования [...] из операнда.
То есть инициализация выполняется
Сделай это так:
template <typename T> constexpr
auto myfunc() noexcept(std::is_nothrow_default_constructible_v<T>)
{
return T{};
}