Почему нет throw или sigsegv при доступе к пустому std:: необязательно?

Пример:

#include <optional>
#include <iostream>

using namespace std;

int main()
{
    optional<int> t{}; // nullopt (empty) by default

    cout << *t << endl;

    return 0;
}

На самом деле эта программа печатает некоторое int (неинициализированное значение типа int). Кроме того, libcxx использует assert-check для доступа к не задействованным значениям.

Почему стандарт не требует метания или сигсегв здесь?

3 ответа

Решение

Почему стандарт не требует метания или сигсегв здесь?

Потому что требование определенного поведения неявно накладывает требование добавить ветку, чтобы проверить, должно ли происходить это поведение - будь то выброс или что-то еще.

Указывая, что поведение не определено, стандарт позволяет реализации не проверять, является ли необязательный параметр пустым при каждом косвенном обращении. Ветвление выполнения потенциально медленнее, чем не ветвление.

Вместо того, чтобы предписывать безопасность, комитет позволил разработчикам стандартной библиотеки выбирать производительность (и простоту). Реализация, которую вы протестировали, похоже, выбрала не создавать исключение или иным образом сообщать вам об ошибке.

C++ охватывает идею неопределенного поведения.

Не все операции C++ имеют поведение, определенное стандартом. Это позволяет компиляторам предполагать, что они никогда не происходят, и во многих случаях может привести к гораздо более быстрому коду.

Здесь, оставляя результат использования std::optional незадействованный undefined, это стоимость доступа к данным, хранящимся в std::optional равна стоимости доступа к данным, не хранящимся в std::optional, Единственными затратами являются дополнительные места, и вы, как программист, пообещаете отследить, занят он или нет.

Теперь компиляторы могут вставлять проверки туда, а некоторые делают в отладочных сборках.

Обратите внимание, что обычно C++ std Типы библиотек включают безопасные и небезопасные методы доступа к данным.

Тот факт, что неверные указатели иногда приводят к появлению сигсева, заключается в том, что большинство ОС защищают адреса около 0 и аварийно завершают программы, которые обращаются к нему. Это потому, что это было дешево, и уловило кучу плохого поведения во многих программах на ассемблере, C и C++.

Если вы хотите опционально бросать когда пусто, используйте .value(), Если нет, используйте operator*, Если вы хотите значение по умолчанию, если его нет, используйте .value_or,

Поскольку это неопределенное поведение, раздел [option.observe]p5 говорит:

Требуется: * это содержит значение.

и нарушение условия require является неопределенным поведением, из [res.on.required#1]p1, которое соответствует общебиблиотечным требованиям:

Нарушение любых предварительных условий, указанных в элементе Requires функции, приводит к неопределенному поведению, если только элемент Throws: функции не вызывает выбрасывание исключения при нарушении предварительного условия.

Таким образом, у вас нет никаких ожиданий относительно результата. Из определения неопределенного поведения:

поведение, к которому этот документ не предъявляет никаких требований

Требование проверки реализации будет стоить, и не все пользователи захотят взять эту стоимость. Так что это становится вопросом качества реализации. Реализация может выполнять проверки в различных режимах работы, например, когда утверждения включены.

У пользователя есть возможность взять стоимость самостоятельно через has_value или value_or. Если пользователь хочет выполнить операцию, которая может выдать, он может использовать значение.

Обратите внимание, что sigsegv, segfaults и т. Д. - это поведение, определяемое реализацией.

Другие вопросы по тегам