Используется ли пустой std::optional UB или нет?

#c

#c

Вопрос:

Следующий код:

     std::optional<std::string> so;
    std::cout << so->size() << std::endl;
    std::cout << so.has_value();
  

выводит:

    0
   0  
  

Мой вопрос в том, безопасно ли вызывать: so->size() для пустого необязательного. Я использовал clang sanitizer, но он не сообщил ни о каком UB в приведенном выше коде.

Комментарии:

1. Это, конечно, не определено так же, как std::string* so=nullptr; so->size(); было бы не определено.

2. @AyxanHaqverdili если это то же самое, что разыменование нулевого указателя, то почему clangs UB sanitizer не распознает его? wandbox.org/permlink/aMv4ZOpXZeH7GgDD Он выбрасывает UndefinedBehaviorSanitizer в тривиальных случаях, как в вашем примере, но оставляет std::optional как корректный, если разыменован, будучи пустым.

3. @mike Вероятно, это сложнее обнаружить, std::optional потому что разыменованное хранилище является внутренним std::optional . Для примера с указателем легко статически определить, какой указатель есть nullptr . Это одна из первых вещей, на обнаружение которых запрограммирован статический анализатор кода. Поскольку std::optional сложнее определить, содержит ли внутреннее хранилище инициализированный объект или нет, если вы не знаете, как std::optional работают внутренние компоненты. Указатель на это хранилище всегда будет действительным указателем на что- то . Трудно понять, является ли это чем-то T или просто хранилищем.

Ответ №1:

Использование operator-> пустого std::optional — это неопределенное поведение, независимо от того, какой тип T находится внутри std::optional<T> .

Согласно cppreference на std::optional<T>::operator-> :

Поведение не определено, если *this не содержит значения.

Комментарии:

1. Решением без неопределенного поведения является функция-член std::optional::value() , которая выдает std::bad_optional_access, если необязательный параметр пуст.

Ответ №2:

Цитирование текущего рабочего проекта C

20.6.3.6 Наблюдатели [необязательно.наблюдать]

 constexpr const T* operator->() const;
constexpr T* operator->();
  

Предварительные условия: * это содержит значение.


Затем:

16.3.2.4 Подробные спецификации [structure.specifications]

Предварительные условия: условия, которые функция предполагает выполнять при каждом ее вызове; нарушение любых предварительных условий приводит к неопределенному поведению.

Таким образом, это неопределенное поведение.

Ответ №3:

Вы вызываете optional конструктор по умолчанию (1 в этой ссылке), который…

  1. Создает объект, который не содержит значения.

Когда вы переходите к разыменованию

Поведение не определено, если *this не содержит значения.

Чего в вашем случае нет. Итак, да, у вас есть UB.