https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87855
Jonathan Wakely <redi at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|NEW |ASSIGNED --- Comment #18 from Jonathan Wakely <redi at gcc dot gnu.org> --- The minimal fix is: PR libstdc++/87855 fix optional for types with non-trivial copy/move When the contained value is not trivially copy (or move) constructible the union's copy (or move) constructor will be deleted, and so the _Optional_payload delegating constructors are invalid. G++ fails to diagnose this because it incorrectly performs copy elision in the delegating constructors. Clang does diagnose it (llvm.org/PR40245). The solution is to avoid performing any copy (or move) when the contained value's copy (or move) constructor isn't trivial. Instead the contained value can be constructed by calling _M_construct. This is OK, because the relevant constructor doesn't need to be constexpr when the contained value isn't trivially copy (or move) constructible. PR libstdc++/87855 * include/std/optional: Adjust whitespace. (_Optional_payload<_Tp, true, C, M>): Use _M_construct to construct the contained object when it isn't trivially copy/move constructible. --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -334,11 +334,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr _Optional_payload(bool __engaged, const _Optional_payload& __other) - : _Optional_payload(__engaged ? - _Optional_payload(__ctor_tag<bool>{}, - __other._M_payload) : - _Optional_payload(__ctor_tag<void>{})) - { } + : _M_engaged(__engaged) + { + if (__engaged) + _M_construct(__other._M_payload); + } constexpr _Optional_payload(bool __engaged, _Optional_payload&& __other) @@ -453,20 +453,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr _Optional_payload(bool __engaged, const _Optional_payload& __other) - : _Optional_payload(__engaged ? - _Optional_payload(__ctor_tag<bool>{}, - __other._M_payload) : - _Optional_payload(__ctor_tag<void>{})) - { } - - constexpr - _Optional_payload(bool __engaged, _Optional_payload&& __other) : _Optional_payload(__engaged ? _Optional_payload(__ctor_tag<bool>{}, - std::move(__other._M_payload)) + __other._M_payload) : _Optional_payload(__ctor_tag<void>{})) { } + constexpr + _Optional_payload(bool __engaged, _Optional_payload&& __other) + : _M_engaged(__engaged) + { + if (__engaged) + _M_construct(std::move(__other._M_payload)); + } + _Optional_payload(const _Optional_payload&) = default; _Optional_payload(_Optional_payload&&) = default; @@ -573,19 +573,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr _Optional_payload(bool __engaged, const _Optional_payload& __other) - : _Optional_payload(__engaged ? - _Optional_payload(__ctor_tag<bool>{}, - __other._M_payload) : - _Optional_payload(__ctor_tag<void>{})) - { } + : _M_engaged(__engaged) + { + if (__engaged) + _M_construct(std::move(__other._M_payload)); + } constexpr _Optional_payload(bool __engaged, _Optional_payload&& __other) - : _Optional_payload(__engaged - ? _Optional_payload(__ctor_tag<bool>{}, - std::move(__other._M_payload)) - : _Optional_payload(__ctor_tag<void>{})) - { } + : _M_engaged(__engaged) + { + if (__engaged) + _M_construct(std::move(__other._M_payload)); + } _Optional_payload(const _Optional_payload&) = default; _Optional_payload(_Optional_payload&&) = default; However, I have a better patch on the way, which hoists all those constructors into a new base so we only need to fi it in one place (not three).