Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::optional: Effective difference between simple and ref-qualified value()

This is an academic question. The std::optional<T> type has a T && value() && method. I have the following definitions:

class A { ... };
void f(A &&a);

And the following program:

std::optional<A> optA;
optA = A(1337);
f(std::move(optA).value()); // OPTION 1
f(std::move(optA.value())); // OPTION 2
std::cout << optA.has_value() << std::endl;

Is there any meaningful difference between OPTION 1 and OPTION 2? For OPTION 1 will I have 1, 0 or unspecified as output? According to my tests has_value() remained true in both cases.

Is there any possible situation where value() && makes a difference over std::move and value()?

like image 720
Notinlist Avatar asked Mar 22 '26 21:03

Notinlist


2 Answers

There's no difference between std::move(optA).value() and std::move(optA.value()). value() just returns a glvalue referring to the contained value, and either you can have value() return an lvalue which is then converted to an xvalue by std::move, or you can call std::move first and have value() give you an xvalue right away (in reality, the conversion from lvalue to xvalue will be occurring somewhere within the value() method itself). The ref-qualified overloads are obviously not very useful in this simple case, but can be useful when the optional was passed by forwarding reference O&& o, and you want std::forward<O>(o).value() to "do the right thing".

like image 166
Brian Bi Avatar answered Mar 24 '26 11:03

Brian Bi


f(std::move(optA).value()); // OPTION 1

You "moved" optA, but this doesn't mean you changed it. Usually you shouldn't use value after it was "moved out", as it's in a valid but indeterminate state. std::move is just a type cast (exactly same as static_cast<A&&>(optA)). Moving constructor wasn't called because no new instance of std::optional<A> was created. Hence has_value returns true.

In this case T && value() && is called indeed.

f(std::move(optA.value())); // OPTION 2

T && value() && is not called here, because optA is not &&. So you get A& and cast it to A&& by std::move, then pass to f which presumably does nothing. optA wasn't changed and still reports that it contains value.

like image 34
Andriy Tylychko Avatar answered Mar 24 '26 10:03

Andriy Tylychko