34

Consider the following example where we parse data and pass the result to the next function:

Content Parse(const std::string& data);
void Process(Content content);

int main()
{
    auto data = ReadData();
    Process(Parse(data));    
}

Now let's change the code using std::optional to handle a failed parsing step:

optional<Content> Parse(const std::string& data);
void Process(Content content);

int main()
{
    auto data = ReadData();
    auto content = Parse(data);
    if (content)
        Process(move(*content));
}

Is it valid to move from optional<T>::value()? If it's ok for std::optional is it valid for boost::optional as well?

Xeo
  • 129,499
  • 52
  • 291
  • 397
hansmaad
  • 18,417
  • 9
  • 53
  • 94
  • I would think this would be fine. The contained value should be in a valid but unspecified state after moving from it. – Vaughn Cato Jul 06 '13 at 11:59

2 Answers2

31

It is valid to move from optional<T>::value() since it returns a mutable reference and the move does not destroy the object. If the optional instance is not engaged, value() will throw a bad_optional_access exception (§20.6.4.5).

You explicitly check whether the option is engaged:

if (content)
    Process(move(*content));

But you don't use the member value() to access the underlying T. Note that value() performs a check internally before returning a valid T&, unlike operator* which has a precondition that the optional instance shall be engaged. This is a subtle difference, but you use the right idiom:

if (o)
  f(*o)

as opposed to

if (o)  // redundant check
  f(o.value())

In Boost, the situation is a little different: first, there exists no member function called value() that provides checked access. (A bad_optional_access exception simply does not exist). The member get() is just an alias for operator* and always relies on the user checking that the optional instance is engaged.

mavam
  • 12,242
  • 10
  • 53
  • 87
  • Does `operator*` have a rvalue reference overload? Ie, `f( *std::move(o) )` -- does that move? I hope so! – Yakk - Adam Nevraumont Jul 15 '13 at 20:12
  • I couldn't find one in [the standard](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf). Have a look at §20.6.4 on page 505 where the interface is described. – mavam Jul 16 '13 at 21:47
  • 2
    Hmm. `value_or` has a `&&` and `&` overload. `value` does not (Why not? Naively, it should). I could see not wanting a `&&` `operator*` in case people are tempted to `*` without checking if the `optional` is engaged first, but with the throwing `value` I cannot see the harm? – Yakk - Adam Nevraumont Jul 16 '13 at 22:08
  • 1
    Looking at a newer C++17 standard draft (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf) and && observer is already there. See page 557. – Jarek C May 20 '20 at 13:03
  • *engaged* confused me, the standard uses the phrase *contains a value* instead. – c z Oct 21 '22 at 14:44
-1

For your current implementation the answer is NO. But if you slightly change it you'll be able to move the value. There are 4 overloads for optional's operator * (as well as for method value()). And there are just 2 of them returning rvalue reference (_Ty && and const _Ty &&) to encapsulated value. But they have signature && and const && respectively. It means you can use them only if your optional variable itself is accessed by rvalue reference. In this case the implementation should look like:

std::optional<Content> Parse(const std::string& data);
void Process(Content content);

int main()
{
    auto data = ReadData();
    auto content = Parse(data);
    if (content)
        Process(*std::move(content));
}
AndyX
  • 81
  • 7