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).