Check out the following code for an example:
#include <iostream>
#include <optional>
class A {
public:
A();
~A();
};
std::optional<A> a;
A::A() { std::cout << a.has_value(); }
A::~A() { std::cout << a.has_value(); }
int main() {
a.emplace();
std::cout << a.has_value();
}
I ended up with something similar, and was caught by surprise that a
had no value in the constructor. Which is probably a good thing since the object isn't fully constructed and my design could be improved. But I didn't find any evidence in cppreference about the way it's supposed to behave according to the standard.
So my question is (mainly due to curiosity): What should be the output in the example code above, and is it specified or implementation-defined (or UB)?
Interestingly, the above code prints (spoiler):
010
for GCC and Cland, and011
for MSVC.
CodePudding user response:
As far as I can tell, the current draft of the standard (as provided by https://timsong-cpp.github.io/cppwp/) is not explicit, but the MSVC behaviour is for me the specified one (whever it is the desirable one during destruction is dubious):
First emplace
contains the following text
Effects: Calls *this = nullopt. Then direct-non-list-initializes the contained value with std::forward(args)....
...
Remarks: If an exception is thrown during the call to T's constructor, *this does not contain a value, and the previous *val (if any) has been destroyed.
It would be a perverse reading to set has_value to false as per the nullopt assignation, then true during the construction (when that action is not specified) and finally reset it if the construction fails with an exception.
The behaviour during the destruction is this one:
Effects: If is_trivially_destructible_v != true and *this contains a value, calls val->T::~T()
There is no mention of changes of has_value
, if one was desired specifying it would be in order.