Учитывать:
template <typename T>
struct S
{
auto Value() const
{
return value;
}
T value;
};
Может ли Value()
когда-нибудь вызвать исключение? Почему или почему нет? Если нет, то можно ли пометить этот метод как noexcept
?
Чего я принципиально не понимаю, так это того, может ли return value;
когда-либо вызывать исключение.
Как с этим справиться - написать noexcept(std::is_nothrow_copy_constructible_v<T>)
.
@Someprogrammerdude, но сам вызов Value никогда не приводит к копированию, верно?
Возврат @Honey по значению приводит к копии.
@PasserBy это именно мое замешательство. Вызов Value сам по себе не приводит к копированию. Так зачем мне проверять is_nothrow_copy_constructible_v?
@wohlstad, что если произойдет копирование?
«Что, если есть исключение копирования» в этом случае не будет, а в тех случаях, когда это произойдет, вы будете двигаться (за исключением некоторых странностей до C++20).
@Дорогая, содержимое члена value
все равно придется скопировать (или переместить) в целевой объект, который вызвал Value()
. Это будет сделано с помощью конструктора, который может выдавать исключение.
Если вы спрашиваете об исключениях в сочетании с копированием, задавайте это в вопросе, а не в комментариях.
В более общем плане: не тратьте время, пытаясь разобраться в таких вещах, как noexcept
, если у вас нет для этого причины. Как видите, это довольно подвержено ошибкам, и компилятор не может это проверить (в отличие, скажем, от override
). И в тех случаях, когда компилятор МОЖЕТ это проверить, он, очевидно, может увидеть это и, возможно, использовать в целях оптимизации, даже если вы этого не указали...
Value()
может вызвать исключение, поэтому не следует помечать его как noexcept
.
Это может произойти, поскольку Value()
возвращает объект T
по значению.
Это означает, что будет создана копия, и конструктор копирования T
может выдать ошибку. Компилятор сгенерирует инструкции копирования в теле метода.
Копирование-эллия не имеет значения, поскольку value
является участником, а не местным или временным.
Но даже если это было актуально, это не меняет этого.
Содержимое члена value
в любом случае будет скопировано (или перемещено) в целевой объект, вызвавший метод. Это можно сделать путем выполнения соответствующего конструктора T
— копирования или перемещения — или оператора присваивания, который может выдать ошибку.
Не имеет значения, действительно ли возвращаемое значение присвоено переменной, как вы можете видеть в демонстрации ниже.
Демо - предоставлено @joergbrech .
пожалуйста, помогите мне понять это. Рассмотрим следующие контексты: auto s = S<ComplexType>{}; Первый контекст таков: auto var = s.Value(); и второй: s.Value(); В обоих случаях простой вызов Value() не вызывает исключения, исключение вызывает оператор присваивания. В случае исключения копирования весь оператор auto var = s.Value(); является причиной исключения, а не s.Value();
@Дорогая, пожалуйста, посмотри добавленную мной демонстрацию Godbolt. В вашем комментарии выше член value
нужно будет каким-то образом перевести в var
. Как я объяснил, это делается путем выполнения конструктора ComplexType
. Даже просто s.Value();
вызывает исключение, как вы можете видеть в демо, поскольку компилятор сгенерирует код для выполнения конструктора.
В операторе return не может быть исключения копирования, поскольку возвращаемое значение является переменной-членом, а не локальной или временной. В теле функции будет копия, поэтому любое возникающее исключение будет возникать внутри функции.
Вы возвращаете значение, а это означает, что значение необходимо скопировать. Если у
T
есть конструктор копирования, он может вызвать исключение.