5

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, and 011 for MSVC.

Paul
  • 6,061
  • 6
  • 39
  • 70
  • not answer the question, but the compiler output shows all compiler agrees there is no value when construction (and msvc contains value in destruction). – apple apple Oct 22 '22 at 12:52
  • Until `A::A()` finishes, there isn't an `A` yet. If `A::A()` throws, `a` never held a value. I'm not sure if this is specified, but it the behavior I would intuitively expect, and it is the behavior that is easiest to implement. I'd only expect `has_value` to be `true` once `emplace` returns. Additionally, you probably can't call `a.value()` until `emplace` returns, supporting that `has_value` should be `false` throughout. – François Andrieux Oct 22 '22 at 12:54
  • 2
    https://cplusplus.github.io/LWG/issue2414 – Language Lawyer Oct 22 '22 at 15:49
  • 2
    Dup of [Shouldn't std::optional contain its value while the emplacing constructor is executing?](https://stackoverflow.com/questions/67316493/shouldnt-stdoptional-contain-its-value-while-the-emplacing-constructor-is-exe) – Language Lawyer Oct 22 '22 at 15:51

1 Answers1

2

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.

  • I'm not sure what you say about "MSVC behaviour is for me the intended one" while they only differs at destruction, no one is "true during the construction". – apple apple Oct 22 '22 at 13:09
  • @appleapple I've edited to be closer to my intended meaning. You are right that to know if that behavior was intended, one would have to dig into the design document and minute notes of the committee. – Nicolas Hamblenne Oct 22 '22 at 13:15