Is use of empty std::optional<string> UB or not?
Asked Answered
W

3

8

The following code:

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

outputs:

   0
   0  

My question is whether its safe to call : so->size() on an empty optional. I used clang sanitizer, but it didnt report any UB in above code.

Wellhead answered 10/11, 2020 at 20:21 Comment(3)
It's of course undefined the same way std::string* so=nullptr; so->size(); would be undefined.Bethlehem
@AyxanHaqverdili if that is the same as dereferencing null pointer, then why clangs UB sanitizer does not recognize it? wandbox.org/permlink/aMv4ZOpXZeH7GgDD It throws UndefinedBehaviorSanitizer on trival cases as in your example, but leaves std::optional as correct if dereferenced while empty.Wellhead
@Wellhead It is probably harder to detect with std::optional because the dereferenced storage is internal to std::optional. For the pointer example, it is easy to statically determine the pointer is nullptr. This is one of the first thing a static code analyzer would be programmed to detect. For std::optional it is harder to determine if the internal storage contains an initialized object or not, unless you know how the internals of std::optional work. A pointer to that storage will always be a valid pointer to something. It's hard to know if that something is a T or just storage.Calabresi
B
14

Using operator-> on an empty std::optional is Undefined Behavior, regardless of what type T is in std::optional<T>.

According to cppreference on std::optional<T>::operator-> :

The behavior is undefined if *this does not contain a value.

Blockhead answered 10/11, 2020 at 20:23 Comment(1)
A solution without undefined behavior is the member function std::optional::value() which throws a std::bad_optional_access if the optional is empty.Adventurer
P
5

Quoting the current C++ working draft

20.6.3.6 Observers [optional.observe]

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

Preconditions: *this contains a value.


Then:

16.3.2.4 Detailed specifications [structure.specifications]

Preconditions: the conditions that the function assumes to hold whenever it is called; violation of any preconditions results in undefined behavior.

Thus, it's undefined behavior.

Protuberance answered 10/11, 2020 at 20:35 Comment(0)
G
4

You're calling the default optional constructor (1 in that link), which...

  1. Constructs an object that does not contain a value.

When you go to dereference...

The behavior is undefined if *this does not contain a value.

Which in your case it doesn't. So yes, you have UB.

Gradate answered 10/11, 2020 at 20:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.