noexcept посещение для std :: variant

Для некоторых стандартных библиотечных классов доступ к частям их содержимого может законно завершиться ошибкой. Обычно у вас есть выбор между некоторым потенциально бросающим методом методом и тем, который помечен noexcept. Последний избавляет от проверки предусловия, поэтому, если вы хотите взять на себя ответственность, вы можете это сделать. Это можно использовать в обстоятельствах, когда использование исключений не разрешено, или при устранении узких мест в производительности.

Пример 1. Доступ к элементу std::vector:

std::vector<int> vec;
vec.at(n) // throws std::out_of_range
vec[n] // potentially UB, thus your own responsibility

Пример 2: std::optional доступ:

std::optional<int> optn;
optn.value() // throws std::bad_optional_access
*optn // potentially UB, thus your own responsibility

Теперь перейдем к std::variant. Прямой доступ к альтернативе отчасти следует этой схеме:

std::variant<std::string, int> var;
std::get<int>(var) // potentially throwing std::bad_variant_access
*std::get_if<int>(&var) // potentially UB, thus your own responsibility

Но на этот раз подпись меняется, мы должны ввести * и &. Обратной стороной этого является то, что мы не получаем семантику автоматического перемещения. Еще одна вещь, о которой нужно помнить ...

Но становится еще хуже, если вы посмотрите на std::visit(Visitor&& vis, Variants&&... vars). Альтернативы ему нет noexcept, хотя только выкидывает

если какой-либо вариант в vars является valueless_by_exception.

Это означает, что при посещении вариантов вы не можете взять на себя ответственность, а если у вас нет выбора и вы должны избегать исключений, вы вообще не можете посещать std::variants со стандартными инструментами! (кроме ужасного обходного пути switching на variant::index())

На мой взгляд, это выглядит как очень плохая оплошность в дизайне ... или есть ли этому причина? И если я прав насчет недосмотра, есть ли инициатива исправить это в стандарте?


person Tobi    schedule 27.12.2018    source источник


Ответы (1)


Это означает, что за посещение вариантов вы не можете взять на себя ответственность.

Что вы можете. Состояние «без значения по исключению» может только случиться, если вы присваиваете или вставляете значение в существующий variant. Более того, по определению это может произойти только в том случае, если во время этих процессов действительно возникает исключение. Это не то состояние, которое когда-либо случается со случайным variant.

Если вы берете на себя ответственность за то, чтобы вы либо никогда не устанавливали / не присваивали вариант, либо чтобы используемые вами типы никогда не применялись в этих обстоятельствах, либо вы реагируете на любые исключения из этого таким образом, что variant, который его спровоцировал, с вами не разговаривают (то есть: если выдается bad_alloc, ваше приложение не улавливает его; оно просто закрывается), тогда вам не нужно беспокоиться об этой возможности.

В принципе, если вы уже кодируете, чтобы избежать исключений, статус visit не-noexcept не имеет значения. Никакой variant никогда не попадет в "бессмысленное исключение", если не будет создано исключение.

person Nicol Bolas    schedule 27.12.2018
comment
Нет: если исключения отключены, std::visit не существует в стандартной библиотеке. - person Tobi; 27.12.2018
comment
@Tobi: Я не знаю компилятора, который бы активно удалял не-noexcept функции при отключении обработки исключений. Насколько я понимаю, отключение обработки исключений либо удаляет throw операторы, либо заставляет их завершить приложение. - person Nicol Bolas; 27.12.2018
comment
Например, Apple Clang не позволяет бросать std::bad_variant_access, если вы нацеливаетесь на macOS 10.13 и более ранние версии (это проблема среды выполнения C ++, см. здесь). Таким образом, std::visit в этом случае не существует. - person Tobi; 27.12.2018
comment
@Tobi: Но это не проблема отключения исключений; это проблема неполной среды выполнения C ++ 17. По сути, я говорю, что это вина того, как iOS / MacOS обрабатывает среду выполнения C ++, а не вина стандарта. - person Nicol Bolas; 27.12.2018
comment
Это всего лишь один пример. Возможно, вы захотите избавиться от дополнительной проверки времени выполнения и сгенерированного кода, генерирующего исключение, по любой причине. Во всех остальных примерах выбор за вами, но не за std::visit. - person Tobi; 27.12.2018
comment
@Tobi: проверка времени выполнения происходит по существу по умолчанию; где-то в visit он должен преобразовать индекс в кусок кода, чтобы получить это значение и вызвать ваш функтор посещения. Таким образом, состояние, не имеющее значения из-за исключения, является просто еще одним состоянием; это ничего не стоит во время выполнения. Просто есть другой индекс, который преобразуется в throw. Дело в том, что это не имеет большого значения. - person Nicol Bolas; 27.12.2018
comment
Это неправда, посмотрите на реализацию libc ++: Он проверяет valueless_by_exception(), а затем переходит к фактической реализации, за исключением которой нет. - person Tobi; 27.12.2018
comment
@Tobi: Это не то, как это должно быть реализовано; просто так это реализуется в некоторых случаях. - person Nicol Bolas; 27.12.2018