Commit 250f5b6c by Ville Voutilainen Committed by Ville Voutilainen

Implement LWG 2900, The copy and move constructors of optional are not constexpr.

Implement LWG 2900, The copy and move constructors
of optional are not constexpr.
* include/std/optional (_Optional_payload): New.
(_Optional_base): Remove the bool parameter.
(_Optional_base<_Tp, false>): Remove.
(_Optional_base()): Adjust.
(_Optional_base(nullopt_t)): Likewise.
(_Optional_base(in_place_t, _Args&&...)): Likewise.
(_Optional_base(in_place_t, initializer_list<_Up>, _Args&&...)):
Likewise.
(_Optional_base(const _Optional_base&)): Likewise.
(_Optional_base(_Optional_base&&)): Likewise.
(operator=(const _Optional_base&)): Likewise.
(operator=(_Optional_base&&)): Likewise.
(~_Optional_base()): Remove.
(_M_is_engaged()): Adjust.
(_M_get()): Likewise.
(_M_construct(_Args&&...)): Likewise.
(_M_destruct()): Likewise.
(_M_reset()): Likewise.
(_Optional_base::_Empty_byte): Remove.
(_Optional_base::_M_empty): Remove.
(_Optional_base::_M_payload): Adjust.
* testsuite/20_util/optional/cons/value_neg.cc: Adjust.
* testsuite/20_util/optional/constexpr/cons/value.cc: Add tests.

From-SVN: r246556
parent 19846619
2017-03-29 Ville Voutilainen <ville.voutilainen@gmail.com>
Implement LWG 2900, The copy and move constructors
of optional are not constexpr.
* include/std/optional (_Optional_payload): New.
(_Optional_base): Remove the bool parameter.
(_Optional_base<_Tp, false>): Remove.
(_Optional_base()): Adjust.
(_Optional_base(nullopt_t)): Likewise.
(_Optional_base(in_place_t, _Args&&...)): Likewise.
(_Optional_base(in_place_t, initializer_list<_Up>, _Args&&...)):
Likewise.
(_Optional_base(const _Optional_base&)): Likewise.
(_Optional_base(_Optional_base&&)): Likewise.
(operator=(const _Optional_base&)): Likewise.
(operator=(_Optional_base&&)): Likewise.
(~_Optional_base()): Remove.
(_M_is_engaged()): Adjust.
(_M_get()): Likewise.
(_M_construct(_Args&&...)): Likewise.
(_M_destruct()): Likewise.
(_M_reset()): Likewise.
(_Optional_base::_Empty_byte): Remove.
(_Optional_base::_M_empty): Remove.
(_Optional_base::_M_payload): Adjust.
* testsuite/20_util/optional/cons/value_neg.cc: Adjust.
* testsuite/20_util/optional/constexpr/cons/value.cc: Add tests.
2017-03-28 Jonathan Wakely <jwakely@redhat.com> 2017-03-28 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/80137 PR libstdc++/80137
......
...@@ -95,178 +95,231 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -95,178 +95,231 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__throw_bad_optional_access() __throw_bad_optional_access()
{ _GLIBCXX_THROW_OR_ABORT(bad_optional_access()); } { _GLIBCXX_THROW_OR_ABORT(bad_optional_access()); }
/**
* @brief Class template that holds the necessary state for @ref optional
* and that has the responsibility for construction and the special members.
*
* Such a separate base class template is necessary in order to
* conditionally enable the special members (e.g. copy/move constructors).
* Note that this means that @ref _Optional_base implements the
* functionality for copy and move assignment, but not for converting
* assignment.
*
* @see optional, _Enable_special_members
*/
template<typename _Tp, bool _ShouldProvideDestructor =
!is_trivially_destructible<_Tp>::value>
class _Optional_base
{
private:
// Remove const to avoid prohibition of reusing object storage for
// const-qualified types in [3.8/9]. This is strictly internal
// and even optional itself is oblivious to it.
using _Stored_type = remove_const_t<_Tp>;
public: // Payload for constexpr optionals.
template <typename _Tp,
bool /*_TrivialCopyMove*/ =
is_trivially_copy_constructible<_Tp>::value
&& is_trivially_move_constructible<_Tp>::value,
bool /*_ShouldProvideDestructor*/ =
is_trivially_destructible<_Tp>::value>
struct _Optional_payload
{
constexpr _Optional_payload()
: _M_empty() {}
// Constructors for disengaged optionals. template<typename... _Args>
constexpr _Optional_base() noexcept constexpr _Optional_payload(in_place_t, _Args&&... __args)
: _M_empty{} { } : _M_payload(std::forward<_Args>(__args)...),
_M_engaged(true)
{}
constexpr _Optional_base(nullopt_t) noexcept template<typename _Up, typename... _Args>
: _Optional_base{} { } constexpr _Optional_payload(std::initializer_list<_Up> __il,
_Args&&... __args)
: _M_payload(__il, std::forward<_Args>(__args)...),
_M_engaged(true) {}
template <class _Up> struct __ctor_tag {};
constexpr _Optional_payload(__ctor_tag<bool>,
const _Tp& __other)
: _M_payload(__other),
_M_engaged(true)
{}
constexpr _Optional_payload(__ctor_tag<void>)
: _M_empty()
{}
constexpr _Optional_payload(__ctor_tag<bool>, _Tp&& __other)
: _M_payload(std::move(__other)),
_M_engaged(true)
{}
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))
: _Optional_payload(__ctor_tag<void>{}))
{}
// Constructors for engaged optionals. using _Stored_type = remove_const_t<_Tp>;
template<typename... _Args, struct _Empty_byte { };
enable_if_t<is_constructible_v<_Tp, _Args&&...>, bool> = false> union {
constexpr explicit _Optional_base(in_place_t, _Args&&... __args) _Empty_byte _M_empty;
: _M_payload(std::forward<_Args>(__args)...), _M_engaged(true) { } _Stored_type _M_payload;
};
bool _M_engaged = false;
};
template<typename _Up, typename... _Args, // Payload for non-constexpr optionals with non-trivial destructor.
enable_if_t<is_constructible_v<_Tp, template <typename _Tp>
initializer_list<_Up>&, struct _Optional_payload<_Tp, false, false>
_Args&&...>, bool> = false> {
constexpr explicit _Optional_base(in_place_t, constexpr _Optional_payload()
initializer_list<_Up> __il, : _M_empty() {}
_Args&&... __args)
: _M_payload(__il, std::forward<_Args>(__args)...),
_M_engaged(true) { }
// Copy and move constructors. template <typename... _Args>
_Optional_base(const _Optional_base& __other) constexpr _Optional_payload(in_place_t, _Args&&... __args)
{ : _M_payload(std::forward<_Args>(__args)...),
if (__other._M_engaged) _M_engaged(true) {}
this->_M_construct(__other._M_get());
}
_Optional_base(_Optional_base&& __other) template<typename _Up, typename... _Args>
noexcept(is_nothrow_move_constructible<_Tp>()) constexpr _Optional_payload(std::initializer_list<_Up> __il,
_Args&&... __args)
: _M_payload(__il, std::forward<_Args>(__args)...),
_M_engaged(true) {}
constexpr
_Optional_payload(bool __engaged, const _Optional_payload& __other)
: _Optional_payload(__other)
{}
constexpr
_Optional_payload(bool __engaged, _Optional_payload&& __other)
: _Optional_payload(std::move(__other))
{}
constexpr _Optional_payload(const _Optional_payload& __other)
{ {
if (__other._M_engaged) if (__other._M_engaged)
this->_M_construct(std::move(__other._M_get())); this->_M_construct(__other._M_payload);
} }
// Assignment operators. constexpr _Optional_payload(_Optional_payload&& __other)
_Optional_base&
operator=(const _Optional_base& __other)
{ {
if (this->_M_engaged && __other._M_engaged) if (__other._M_engaged)
this->_M_get() = __other._M_get(); this->_M_construct(std::move(__other._M_payload));
else
{
if (__other._M_engaged)
this->_M_construct(__other._M_get());
else
this->_M_reset();
}
return *this;
} }
_Optional_base& using _Stored_type = remove_const_t<_Tp>;
operator=(_Optional_base&& __other) struct _Empty_byte { };
noexcept(__and_<is_nothrow_move_constructible<_Tp>, union {
is_nothrow_move_assignable<_Tp>>()) _Empty_byte _M_empty;
{ _Stored_type _M_payload;
if (this->_M_engaged && __other._M_engaged) };
this->_M_get() = std::move(__other._M_get()); bool _M_engaged = false;
else
{
if (__other._M_engaged)
this->_M_construct(std::move(__other._M_get()));
else
this->_M_reset();
}
return *this;
}
// Destructor. ~_Optional_payload()
~_Optional_base()
{ {
if (this->_M_engaged) if (_M_engaged)
this->_M_payload.~_Stored_type(); _M_payload.~_Stored_type();
} }
// The following functionality is also needed by optional, hence the
// protected accessibility.
protected:
constexpr bool _M_is_engaged() const noexcept
{ return this->_M_engaged; }
// The _M_get operations have _M_engaged as a precondition.
constexpr _Tp&
_M_get() noexcept
{ return _M_payload; }
constexpr const _Tp&
_M_get() const noexcept
{ return _M_payload; }
// The _M_construct operation has !_M_engaged as a precondition
// while _M_destruct has _M_engaged as a precondition.
template<typename... _Args> template<typename... _Args>
void void
_M_construct(_Args&&... __args) _M_construct(_Args&&... __args)
noexcept(is_nothrow_constructible<_Stored_type, _Args...>()) noexcept(is_nothrow_constructible<_Stored_type, _Args...>())
{ {
::new (std::__addressof(this->_M_payload)) ::new ((void *) std::__addressof(this->_M_payload))
_Stored_type(std::forward<_Args>(__args)...); _Stored_type(std::forward<_Args>(__args)...);
this->_M_engaged = true; this->_M_engaged = true;
} }
};
void // Payload for non-constexpr optionals with trivial destructor.
_M_destruct() template <typename _Tp>
struct _Optional_payload<_Tp, false, true>
{
constexpr _Optional_payload()
: _M_empty() {}
template <typename... _Args>
constexpr _Optional_payload(in_place_t, _Args&&... __args)
: _M_payload(std::forward<_Args>(__args)...),
_M_engaged(true) {}
template<typename _Up, typename... _Args>
constexpr _Optional_payload(std::initializer_list<_Up> __il,
_Args&&... __args)
: _M_payload(__il, std::forward<_Args>(__args)...),
_M_engaged(true) {}
constexpr
_Optional_payload(bool __engaged, const _Optional_payload& __other)
: _Optional_payload(__other)
{}
constexpr
_Optional_payload(bool __engaged, _Optional_payload&& __other)
: _Optional_payload(std::move(__other))
{}
constexpr _Optional_payload(const _Optional_payload& __other)
{ {
this->_M_engaged = false; if (__other._M_engaged)
this->_M_payload.~_Stored_type(); this->_M_construct(__other._M_payload);
} }
// _M_reset is a 'safe' operation with no precondition. constexpr _Optional_payload(_Optional_payload&& __other)
void
_M_reset()
{ {
if (this->_M_engaged) if (__other._M_engaged)
this->_M_destruct(); this->_M_construct(std::move(__other._M_payload));
} }
private: using _Stored_type = remove_const_t<_Tp>;
struct _Empty_byte { }; struct _Empty_byte { };
union { union {
_Empty_byte _M_empty; _Empty_byte _M_empty;
_Stored_type _M_payload; _Stored_type _M_payload;
}; };
bool _M_engaged = false; bool _M_engaged = false;
template<typename... _Args>
void
_M_construct(_Args&&... __args)
noexcept(is_nothrow_constructible<_Stored_type, _Args...>())
{
::new ((void *) std::__addressof(this->_M_payload))
_Stored_type(std::forward<_Args>(__args)...);
this->_M_engaged = true;
}
}; };
/// Partial specialization that is exactly identical to the primary template /**
/// save for not providing a destructor, to fulfill triviality requirements. * @brief Class template that holds the necessary state for @ref optional
* and that has the responsibility for construction and the special members.
*
* Such a separate base class template is necessary in order to
* conditionally enable the special members (e.g. copy/move constructors).
* Note that this means that @ref _Optional_base implements the
* functionality for copy and move assignment, but not for converting
* assignment.
*
* @see optional, _Enable_special_members
*/
template<typename _Tp> template<typename _Tp>
class _Optional_base<_Tp, false> class _Optional_base
{ {
private: private:
// Remove const to avoid prohibition of reusing object storage for
// const-qualified types in [3.8/9]. This is strictly internal
// and even optional itself is oblivious to it.
using _Stored_type = remove_const_t<_Tp>; using _Stored_type = remove_const_t<_Tp>;
public: public:
// Constructors for disengaged optionals.
constexpr _Optional_base() noexcept constexpr _Optional_base() noexcept
: _M_empty{} { } { }
constexpr _Optional_base(nullopt_t) noexcept constexpr _Optional_base(nullopt_t) noexcept
: _Optional_base{} { } { }
// Constructors for engaged optionals.
template<typename... _Args, template<typename... _Args,
enable_if_t<is_constructible_v<_Tp, _Args&&...>, bool> = false> enable_if_t<is_constructible_v<_Tp, _Args&&...>, bool> = false>
constexpr explicit _Optional_base(in_place_t, _Args&&... __args) constexpr explicit _Optional_base(in_place_t, _Args&&... __args)
: _M_payload(std::forward<_Args>(__args)...), _M_engaged(true) { } : _M_payload(in_place,
std::forward<_Args>(__args)...) { }
template<typename _Up, typename... _Args, template<typename _Up, typename... _Args,
enable_if_t<is_constructible_v<_Tp, enable_if_t<is_constructible_v<_Tp,
...@@ -275,35 +328,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -275,35 +328,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
constexpr explicit _Optional_base(in_place_t, constexpr explicit _Optional_base(in_place_t,
initializer_list<_Up> __il, initializer_list<_Up> __il,
_Args&&... __args) _Args&&... __args)
: _M_payload(__il, std::forward<_Args>(__args)...), : _M_payload(in_place,
_M_engaged(true) { } __il, std::forward<_Args>(__args)...)
{ }
_Optional_base(const _Optional_base& __other) // Copy and move constructors.
{ constexpr _Optional_base(const _Optional_base& __other)
if (__other._M_engaged) : _M_payload(__other._M_payload._M_engaged,
this->_M_construct(__other._M_get()); __other._M_payload)
} { }
_Optional_base(_Optional_base&& __other) constexpr _Optional_base(_Optional_base&& __other)
noexcept(is_nothrow_move_constructible<_Tp>()) noexcept(is_nothrow_move_constructible<_Tp>())
{ : _M_payload(__other._M_payload._M_engaged,
if (__other._M_engaged) std::move(__other._M_payload))
this->_M_construct(std::move(__other._M_get())); { }
}
// Assignment operators.
_Optional_base& _Optional_base&
operator=(const _Optional_base& __other) operator=(const _Optional_base& __other)
{ {
if (this->_M_engaged && __other._M_engaged) if (this->_M_payload._M_engaged && __other._M_payload._M_engaged)
this->_M_get() = __other._M_get(); this->_M_get() = __other._M_get();
else else
{ {
if (__other._M_engaged) if (__other._M_payload._M_engaged)
this->_M_construct(__other._M_get()); this->_M_construct(__other._M_get());
else else
this->_M_reset(); this->_M_reset();
} }
return *this;
return *this;
} }
_Optional_base& _Optional_base&
...@@ -311,65 +366,61 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -311,65 +366,61 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(__and_<is_nothrow_move_constructible<_Tp>, noexcept(__and_<is_nothrow_move_constructible<_Tp>,
is_nothrow_move_assignable<_Tp>>()) is_nothrow_move_assignable<_Tp>>())
{ {
if (this->_M_engaged && __other._M_engaged) if (this->_M_payload._M_engaged && __other._M_payload._M_engaged)
this->_M_get() = std::move(__other._M_get()); this->_M_get() = std::move(__other._M_get());
else else
{ {
if (__other._M_engaged) if (__other._M_payload._M_engaged)
this->_M_construct(std::move(__other._M_get())); this->_M_construct(std::move(__other._M_get()));
else else
this->_M_reset(); this->_M_reset();
} }
return *this; return *this;
} }
// The following functionality is also needed by optional, hence the
// Sole difference // protected accessibility.
// ~_Optional_base() noexcept = default;
protected: protected:
constexpr bool _M_is_engaged() const noexcept constexpr bool _M_is_engaged() const noexcept
{ return this->_M_engaged; } { return this->_M_payload._M_engaged; }
// The _M_get operations have _M_engaged as a precondition.
constexpr _Tp& constexpr _Tp&
_M_get() noexcept _M_get() noexcept
{ return _M_payload; } { return this->_M_payload._M_payload; }
constexpr const _Tp& constexpr const _Tp&
_M_get() const noexcept _M_get() const noexcept
{ return _M_payload; } { return this->_M_payload._M_payload; }
// The _M_construct operation has !_M_engaged as a precondition
// while _M_destruct has _M_engaged as a precondition.
template<typename... _Args> template<typename... _Args>
void void
_M_construct(_Args&&... __args) _M_construct(_Args&&... __args)
noexcept(is_nothrow_constructible<_Stored_type, _Args...>()) noexcept(is_nothrow_constructible<_Stored_type, _Args...>())
{ {
::new (std::__addressof(this->_M_payload)) ::new (std::__addressof(this->_M_payload._M_payload))
_Stored_type(std::forward<_Args>(__args)...); _Stored_type(std::forward<_Args>(__args)...);
this->_M_engaged = true; this->_M_payload._M_engaged = true;
} }
void void
_M_destruct() _M_destruct()
{ {
this->_M_engaged = false; this->_M_payload._M_engaged = false;
this->_M_payload.~_Stored_type(); this->_M_payload._M_payload.~_Stored_type();
} }
// _M_reset is a 'safe' operation with no precondition.
void void
_M_reset() _M_reset()
{ {
if (this->_M_engaged) if (this->_M_payload._M_engaged)
this->_M_destruct(); this->_M_destruct();
} }
private: private:
struct _Empty_byte { }; _Optional_payload<_Tp> _M_payload;
union
{
_Empty_byte _M_empty;
_Stored_type _M_payload;
};
bool _M_engaged = false;
}; };
template<typename _Tp> template<typename _Tp>
......
...@@ -37,8 +37,8 @@ int main() ...@@ -37,8 +37,8 @@ int main()
std::optional<std::unique_ptr<int>> oup2 = new int; // { dg-error "conversion" } std::optional<std::unique_ptr<int>> oup2 = new int; // { dg-error "conversion" }
struct U { explicit U(std::in_place_t); }; struct U { explicit U(std::in_place_t); };
std::optional<U> ou(std::in_place); // { dg-error "no matching" } std::optional<U> ou(std::in_place); // { dg-error "no matching" }
// { dg-error "no type" "" { target { *-*-* } } 437 } // { dg-error "no type" "" { target { *-*-* } } 488 }
// { dg-error "no type" "" { target { *-*-* } } 447 } // { dg-error "no type" "" { target { *-*-* } } 498 }
// { dg-error "no type" "" { target { *-*-* } } 504 } // { dg-error "no type" "" { target { *-*-* } } 555 }
} }
} }
...@@ -66,4 +66,21 @@ int main() ...@@ -66,4 +66,21 @@ int main()
static_assert( o, "" ); static_assert( o, "" );
static_assert( *o == 0x1234ABCD, "" ); static_assert( *o == 0x1234ABCD, "" );
} }
{
constexpr std::optional<long> o = 42;
constexpr std::optional<long> o2{o};
constexpr std::optional<long> o3(o);
constexpr std::optional<long> o4 = o;
constexpr std::optional<long> o5;
constexpr std::optional<long> o6{o5};
constexpr std::optional<long> o7(o5);
constexpr std::optional<long> o8 = o5;
constexpr std::optional<long> o9{std::move(o)};
constexpr std::optional<long> o10(std::move(o));
constexpr std::optional<long> o11 = std::move(o);
constexpr std::optional<long> o12;
constexpr std::optional<long> o13{std::move(o5)};
constexpr std::optional<long> o14(std::move(o5));
constexpr std::optional<long> o15 = std::move(o5);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment