I'm very confused about how std::optional's copy constructor is supposed to be implemented to meet the requirements of being constexpr.
Note there are many other questions on Stackoverflow asking something similar, such as:
How to implement std::optional's copy constructor?
std::optional implemented as union vs char[]/aligned_storage
However, neither of these questions are actually asking about the COPY CONSTRUCTOR. I am specifically asking about the copy constructor, with a function signature (from https://en.cppreference.com/w/cpp/utility/optional/optional) as follows:
constexpr optional( const optional& other );
Now, I've read enough about std::optional to know the basics. A typical mistake implementors make is to try and implement it with std::aligned_storage. Since placement new cannot work in a constexpr (at least in C++17), this won't work. Instead, a union type needs to be used so it can be directly constructed. Something like:
struct dummy_type {};
union optional_impl
{
dummy_type m_dummy;
T m_value;
};
Okay, but still... I still don't see how we are supposed to meet the requirements of implementing the copy constructor as constexpr. The problem is that in the copy constructor, we need to check if other.has_value() is true. If it is, we want to directly copy *other, otherwise we just want to initialize m_dummy. But how can we express this conditional decision in a constexpr copy constructor?
constexpr optional( const optional& other ) : m_dummy{}
{
if (other.has_value()) new (&m_value) T(*other); // Wrong! Can't use placement new
}
The only way I can see this working is using placement new.
So I checked some actual implemenations, like the gcc implementation here:
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/optional#L248
And indeed... they just use placement new. And in fact, the copy constructor isn't event constexpr (which I guess is a defect).
Here's another implementation:
https://github.com/akrzemi1/Optional/blob/master/optional.hpp#L416
And again, they just use placement new.
So how can optional(const optional&) ever be implemented as a constexpr? Is this a defect in the standard or something?