Use std::optional as a regular pointer vs use has_value() and value
Asked Answered
F

2

12

std::optional can use the syntax to access its value similar to a normal pointer, like .

std::optional<string> some_str;
if (some_str)
    (*some_str).c_str();

but it also has two functions, has_value() and value() to provide access to its value and to check if the value exists.

std::optional<string> some_str;
if (some_str.has_value())
    some_str.value().c_str();

I am wondering what is the difference between these two Is it for?
1. more verbose
2. performance?
3. better logging and debugging? value() will throw exception.

Fania answered 10/5, 2019 at 17:50 Comment(2)
This is pretty much the same debate as vector::operator[] vs vector::at.Nelsonnema
Possible duplicate of vector::at vs. vector::operator Edit : I don't know how to escape the [] in a hyperlink...Nelsonnema
R
17

There are two separate things here.

First, explicit operator bool() const vs bool has_value() const. These are exactly synonyms. They mean the exact same thing. Use whichever one you prefer.

Second, T& value() vs T& operator*(). This is the same as vector::at vs. vector::operator[]. The former has no preconditions - it checks and throws - the latter has preconditions - it is undefined behavior if the optional is disengaged.

Redvers answered 10/5, 2019 at 18:3 Comment(0)
U
7

std::optional (no experimental, C++17 is upon us) defines two different interfaces to access it's optional member: the checked access (std::optional::value) and the unchecked access (operator*).

Using checked access has two potential drawbacks - first, it slows execution down because branch is executed, and second, in case of value not being there it will throw an exception, which could be undesirable in terms of control flow. While the second issue can be alleviated via explicit check for value present (std::optional::has_value) before calling value, the run-time performance cost could still be there.

An unchecked access doesn't have performance cost associated to that, but can only be safely used if you know that the value is there by some other means (i.e. you checked has_value before). Otherwise, behavior of the program is undefined, and we do not want that.

From the above, it should be obvious that the second snippet you have shown is excessive:

if (some_str.has_value())
    some_str.value().c_str();

Does the check for value presence twice (at least semantically, compilers are likely to be able to optimize the second check away). Still, the clearer code would be

if (some_str.has_value())
    some_str->c_str();
Upcoming answered 10/5, 2019 at 18:2 Comment(4)
Technically, compilers are free to detect that we could not have disengaged the optional between the check and the .value() call. See godbolt.org/z/2TEJH5 for a completely optimized-away optional, or here for an optimize call to .value() because the compiler saw we already checked has_value.Remote
@Yakk-AdamNevraumont - that's why I said "at least semantically, compilers are likely to optimize the check away"Upcoming
Sure, except now the code has no "the run-time performance cost will still be there" -- the run-time cost can actually be eliminated by the compiler, so long as it can prove you checked the optional was engaged. Things only get interesting when you have independent knowledge of the engaged state of the optional that the compiler cannot practically deduce; but that is rarely a great situation, as your code becomes fragile, difficult to understand, and hard to get right.Remote
@Yakk-AdamNevraumont fair enough, edited the answer.Upcoming

© 2022 - 2025 — McMap. All rights reserved.