[libcxx][optional] adds missing constexpr operations

Makes the following operations constexpr:
  * `std::swap(optional, optional)`
  * `optional(optional<U> const&)`
  * `optional(optional<U>&&)`
  * `~optional()`
  * `operator=(nullopt_t)`
  * `operator=(U&&)`
  * `operator=(optional<U> const&)`
  * `operator=(optional<U>&&)`
  * `emplace(Args&&...)`
  * `emplace(initializer_list<U>, Args&&...)`
  * `swap(optional&)`
  * `reset()`

P2231 has been accepted by plenary, with the committee recommending
implementers retroactively apply to C++20. It's necessary for us to
implement _`semiregular-box`_ and _`non-propagating-cache`_, both of
which are required for ranges (otherwise we'll need to reimplement
`std::optional` with these members `constexpr`ified).

Differential Revision: https://reviews.llvm.org/D102119
This commit is contained in:
Christopher Di Bella 2021-05-09 01:30:32 +00:00
parent b5d6da3587
commit 546449938a
14 changed files with 443 additions and 266 deletions

View File

@ -193,3 +193,4 @@
"`P2102 <https://wg21.link/P2102>`__","LWG","Make 'implicit expression variations' more explicit (Wording for US185)","Prague","* *",""
"`P2106 <https://wg21.link/P2106>`__","LWG","Alternative wording for GB315 and GB316","Prague","* *",""
"`P2116 <https://wg21.link/P2116>`__","LWG","Remove tuple-like protocol support from fixed-extent span","Prague","|Complete|","11.0"
"`P2231 <https://wg21.link/P2231>`__","LWG","Missing constexpr in std::optional and std::variant","February 2021","|In progress|","13.0"

1 Paper # Group Paper Name Meeting Status First released version
193 `P2102 <https://wg21.link/P2102>`__ LWG Make 'implicit expression variations' more explicit (Wording for US185) Prague * *
194 `P2106 <https://wg21.link/P2106>`__ LWG Alternative wording for GB315 and GB316 Prague * *
195 `P2116 <https://wg21.link/P2116>`__ LWG Remove tuple-like protocol support from fixed-extent span Prague |Complete| 11.0
196 `P2231 <https://wg21.link/P2231>`__ LWG Missing constexpr in std::optional and std::variant February 2021 |In progress| 13.0

View File

@ -69,7 +69,7 @@ namespace std {
template <class T, class U> constexpr bool operator>=(const T&, const optional<U>&);
// 23.6.9, specialized algorithms
template <class T> void swap(optional<T>&, optional<T>&) noexcept(see below );
template <class T> void swap(optional<T>&, optional<T>&) noexcept(see below ); // constexpr in C++20
template <class T> constexpr optional<see below > make_optional(T&&);
template <class T, class... Args>
constexpr optional<T> make_optional(Args&&... args);
@ -95,26 +95,26 @@ namespace std {
template <class U = T>
constexpr EXPLICIT optional(U &&);
template <class U>
constexpr EXPLICIT optional(const optional<U> &);
EXPLICIT optional(const optional<U> &); // constexpr in C++20
template <class U>
constexpr EXPLICIT optional(optional<U> &&);
EXPLICIT optional(optional<U> &&); // constexpr in C++20
// 23.6.3.2, destructor
~optional();
~optional(); // constexpr in C++20
// 23.6.3.3, assignment
optional &operator=(nullopt_t) noexcept;
optional &operator=(const optional &); // constexpr in C++20
optional &operator=(optional &&) noexcept(see below); // constexpr in C++20
template <class U = T> optional &operator=(U &&);
template <class U> optional &operator=(const optional<U> &);
template <class U> optional &operator=(optional<U> &&);
template <class... Args> T& emplace(Args &&...);
optional &operator=(nullopt_t) noexcept; // constexpr in C++20
optional &operator=(const optional &); // constexpr in C++20
optional &operator=(optional &&) noexcept(see below); // constexpr in C++20
template <class U = T> optional &operator=(U &&); // constexpr in C++20
template <class U> optional &operator=(const optional<U> &); // constexpr in C++20
template <class U> optional &operator=(optional<U> &&); // constexpr in C++20
template <class... Args> T& emplace(Args &&...); // constexpr in C++20
template <class U, class... Args>
T& emplace(initializer_list<U>, Args &&...);
T& emplace(initializer_list<U>, Args &&...); // constexpr in C++20
// 23.6.3.4, swap
void swap(optional &) noexcept(see below );
void swap(optional &) noexcept(see below ); // constexpr in C++20
// 23.6.3.5, observers
constexpr T const *operator->() const;
@ -133,7 +133,7 @@ namespace std {
template <class U> constexpr T value_or(U &&) &&;
// 23.6.3.6, modifiers
void reset() noexcept;
void reset() noexcept; // constexpr in C++20
private:
T *val; // exposition only
@ -221,7 +221,7 @@ struct __optional_destruct_base<_Tp, false>
bool __engaged_;
_LIBCPP_INLINE_VISIBILITY
~__optional_destruct_base()
_LIBCPP_CONSTEXPR_AFTER_CXX17 ~__optional_destruct_base()
{
if (__engaged_)
__val_.~value_type();
@ -239,7 +239,7 @@ struct __optional_destruct_base<_Tp, false>
__engaged_(true) {}
_LIBCPP_INLINE_VISIBILITY
void reset() noexcept
_LIBCPP_CONSTEXPR_AFTER_CXX17 void reset() noexcept
{
if (__engaged_)
{
@ -274,7 +274,7 @@ struct __optional_destruct_base<_Tp, true>
__engaged_(true) {}
_LIBCPP_INLINE_VISIBILITY
void reset() noexcept
_LIBCPP_CONSTEXPR_AFTER_CXX17 void reset() noexcept
{
if (__engaged_)
{
@ -319,16 +319,20 @@ struct __optional_storage_base : __optional_destruct_base<_Tp>
template <class... _Args>
_LIBCPP_INLINE_VISIBILITY
void __construct(_Args&&... __args)
_LIBCPP_CONSTEXPR_AFTER_CXX17 void __construct(_Args&&... __args)
{
_LIBCPP_ASSERT(!has_value(), "__construct called for engaged __optional_storage");
#if _LIBCPP_STD_VER > 17
_VSTD::construct_at(_VSTD::addressof(this->__val_), _VSTD::forward<_Args>(__args)...);
#else
::new ((void*)_VSTD::addressof(this->__val_)) value_type(_VSTD::forward<_Args>(__args)...);
#endif
this->__engaged_ = true;
}
template <class _That>
_LIBCPP_INLINE_VISIBILITY
void __construct_from(_That&& __opt)
_LIBCPP_CONSTEXPR_AFTER_CXX17 void __construct_from(_That&& __opt)
{
if (__opt.has_value())
__construct(_VSTD::forward<_That>(__opt).__get());
@ -336,7 +340,7 @@ struct __optional_storage_base : __optional_destruct_base<_Tp>
template <class _That>
_LIBCPP_INLINE_VISIBILITY
void __assign_from(_That&& __opt)
_LIBCPP_CONSTEXPR_AFTER_CXX17 void __assign_from(_That&& __opt)
{
if (this->__engaged_ == __opt.has_value())
{
@ -394,7 +398,7 @@ struct __optional_storage_base<_Tp, true>
}
_LIBCPP_INLINE_VISIBILITY
void reset() noexcept { __value_ = nullptr; }
_LIBCPP_CONSTEXPR_AFTER_CXX17 void reset() noexcept { __value_ = nullptr; }
_LIBCPP_INLINE_VISIBILITY
constexpr bool has_value() const noexcept
@ -410,7 +414,7 @@ struct __optional_storage_base<_Tp, true>
template <class _UArg>
_LIBCPP_INLINE_VISIBILITY
void __construct(_UArg&& __val)
_LIBCPP_CONSTEXPR_AFTER_CXX17 void __construct(_UArg&& __val)
{
_LIBCPP_ASSERT(!has_value(), "__construct called for engaged __optional_storage");
static_assert(__can_bind_reference<_UArg>(),
@ -421,7 +425,7 @@ struct __optional_storage_base<_Tp, true>
template <class _That>
_LIBCPP_INLINE_VISIBILITY
void __construct_from(_That&& __opt)
_LIBCPP_CONSTEXPR_AFTER_CXX17 void __construct_from(_That&& __opt)
{
if (__opt.has_value())
__construct(_VSTD::forward<_That>(__opt).__get());
@ -429,7 +433,7 @@ struct __optional_storage_base<_Tp, true>
template <class _That>
_LIBCPP_INLINE_VISIBILITY
void __assign_from(_That&& __opt)
_LIBCPP_CONSTEXPR_AFTER_CXX17 void __assign_from(_That&& __opt)
{
if (has_value() == __opt.has_value())
{
@ -461,7 +465,7 @@ struct __optional_copy_base<_Tp, false> : __optional_storage_base<_Tp>
__optional_copy_base() = default;
_LIBCPP_INLINE_VISIBILITY
__optional_copy_base(const __optional_copy_base& __opt)
_LIBCPP_CONSTEXPR_AFTER_CXX17 __optional_copy_base(const __optional_copy_base& __opt)
{
this->__construct_from(__opt);
}
@ -492,7 +496,7 @@ struct __optional_move_base<_Tp, false> : __optional_copy_base<_Tp>
__optional_move_base(const __optional_move_base&) = default;
_LIBCPP_INLINE_VISIBILITY
__optional_move_base(__optional_move_base&& __opt)
_LIBCPP_CONSTEXPR_AFTER_CXX17 __optional_move_base(__optional_move_base&& __opt)
noexcept(is_nothrow_move_constructible_v<value_type>)
{
this->__construct_from(_VSTD::move(__opt));
@ -526,7 +530,7 @@ struct __optional_copy_assign_base<_Tp, false> : __optional_move_base<_Tp>
__optional_copy_assign_base(__optional_copy_assign_base&&) = default;
_LIBCPP_INLINE_VISIBILITY
__optional_copy_assign_base& operator=(const __optional_copy_assign_base& __opt)
_LIBCPP_CONSTEXPR_AFTER_CXX17 __optional_copy_assign_base& operator=(const __optional_copy_assign_base& __opt)
{
this->__assign_from(__opt);
return *this;
@ -561,7 +565,7 @@ struct __optional_move_assign_base<_Tp, false> : __optional_copy_assign_base<_Tp
__optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;
_LIBCPP_INLINE_VISIBILITY
__optional_move_assign_base& operator=(__optional_move_assign_base&& __opt)
_LIBCPP_CONSTEXPR_AFTER_CXX17 __optional_move_assign_base& operator=(__optional_move_assign_base&& __opt)
noexcept(is_nothrow_move_assignable_v<value_type> &&
is_nothrow_move_constructible_v<value_type>)
{
@ -728,7 +732,7 @@ public:
_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_implicit<_Up>()
, int> = 0>
_LIBCPP_INLINE_VISIBILITY
optional(const optional<_Up>& __v)
_LIBCPP_CONSTEXPR_AFTER_CXX17 optional(const optional<_Up>& __v)
{
this->__construct_from(__v);
}
@ -736,7 +740,7 @@ public:
_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_explicit<_Up>()
, int> = 0>
_LIBCPP_INLINE_VISIBILITY
explicit optional(const optional<_Up>& __v)
_LIBCPP_CONSTEXPR_AFTER_CXX17 explicit optional(const optional<_Up>& __v)
{
this->__construct_from(__v);
}
@ -746,7 +750,7 @@ public:
_CheckOptionalLikeCtor<_Up, _Up &&>::template __enable_implicit<_Up>()
, int> = 0>
_LIBCPP_INLINE_VISIBILITY
optional(optional<_Up>&& __v)
_LIBCPP_CONSTEXPR_AFTER_CXX17 optional(optional<_Up>&& __v)
{
this->__construct_from(_VSTD::move(__v));
}
@ -754,13 +758,13 @@ public:
_CheckOptionalLikeCtor<_Up, _Up &&>::template __enable_explicit<_Up>()
, int> = 0>
_LIBCPP_INLINE_VISIBILITY
explicit optional(optional<_Up>&& __v)
_LIBCPP_CONSTEXPR_AFTER_CXX17 explicit optional(optional<_Up>&& __v)
{
this->__construct_from(_VSTD::move(__v));
}
_LIBCPP_INLINE_VISIBILITY
optional& operator=(nullopt_t) noexcept
_LIBCPP_CONSTEXPR_AFTER_CXX17 optional& operator=(nullopt_t) noexcept
{
reset();
return *this;
@ -783,7 +787,7 @@ public:
>::value>
>
_LIBCPP_INLINE_VISIBILITY
optional&
_LIBCPP_CONSTEXPR_AFTER_CXX17 optional&
operator=(_Up&& __v)
{
if (this->has_value())
@ -798,7 +802,7 @@ public:
_CheckOptionalLikeAssign<_Up, _Up const&>::template __enable_assign<_Up>()
, int> = 0>
_LIBCPP_INLINE_VISIBILITY
optional&
_LIBCPP_CONSTEXPR_AFTER_CXX17 optional&
operator=(const optional<_Up>& __v)
{
this->__assign_from(__v);
@ -810,7 +814,7 @@ public:
_CheckOptionalLikeCtor<_Up, _Up &&>::template __enable_assign<_Up>()
, int> = 0>
_LIBCPP_INLINE_VISIBILITY
optional&
_LIBCPP_CONSTEXPR_AFTER_CXX17 optional&
operator=(optional<_Up>&& __v)
{
this->__assign_from(_VSTD::move(__v));
@ -824,7 +828,7 @@ public:
>
>
_LIBCPP_INLINE_VISIBILITY
_Tp &
_LIBCPP_CONSTEXPR_AFTER_CXX17 _Tp &
emplace(_Args&&... __args)
{
reset();
@ -839,7 +843,7 @@ public:
>
>
_LIBCPP_INLINE_VISIBILITY
_Tp &
_LIBCPP_CONSTEXPR_AFTER_CXX17 _Tp &
emplace(initializer_list<_Up> __il, _Args&&... __args)
{
reset();
@ -848,7 +852,7 @@ public:
}
_LIBCPP_INLINE_VISIBILITY
void swap(optional& __opt)
_LIBCPP_CONSTEXPR_AFTER_CXX17 void swap(optional& __opt)
noexcept(is_nothrow_move_constructible_v<value_type> &&
is_nothrow_swappable_v<value_type>)
{
@ -1006,7 +1010,7 @@ public:
private:
template <class _Up>
_LIBCPP_INLINE_VISIBILITY
static _Up*
static _LIBCPP_CONSTEXPR_AFTER_CXX17 _Up*
__operator_arrow(true_type, _Up& __x)
{
return _VSTD::addressof(__x);
@ -1367,7 +1371,7 @@ operator>=(const _Tp& __v, const optional<_Up>& __x)
template <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
_EnableIf<
is_move_constructible_v<_Tp> && is_swappable_v<_Tp>,
void

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14
// UNSUPPORTED: gcc-10
// <optional>
// template <class... Args> T& optional<T>::emplace(Args&&... args);
@ -26,11 +27,11 @@ class X
int i_;
int j_ = 0;
public:
X() : i_(0) {}
X(int i) : i_(i) {}
X(int i, int j) : i_(i), j_(j) {}
constexpr X() : i_(0) {}
constexpr X(int i) : i_(i) {}
constexpr X(int i, int j) : i_(i), j_(j) {}
friend bool operator==(const X& x, const X& y)
constexpr friend bool operator==(const X& x, const X& y)
{return x.i_ == y.i_ && x.j_ == y.j_;}
};
@ -46,7 +47,7 @@ public:
bool Y::dtor_called = false;
template <class T>
void test_one_arg() {
constexpr bool test_one_arg() {
using Opt = std::optional<T>;
{
Opt opt;
@ -80,11 +81,12 @@ void test_one_arg() {
assert(*opt == T(1));
assert(&v == &*opt);
}
return true;
}
template <class T>
void test_multi_arg()
constexpr bool test_multi_arg()
{
test_one_arg<T>();
using Opt = std::optional<T>;
@ -112,6 +114,7 @@ void test_multi_arg()
assert( v == T(5)); // T sets its value to the size of the init list
assert(*opt == T(5)); // T sets its value to the size of the init list
}
return true;
}
template <class T>
@ -206,7 +209,17 @@ void test_on_test_type() {
}
}
constexpr bool test_empty_emplace()
{
optional<const int> opt;
auto &v = opt.emplace(42);
static_assert( std::is_same_v<const int&, decltype(v)>, "" );
assert(*opt == 42);
assert( v == 42);
opt.emplace();
assert(*opt == 0);
return true;
}
int main(int, char**)
{
@ -218,31 +231,44 @@ int main(int, char**)
using T = int;
test_one_arg<T>();
test_one_arg<const T>();
#if TEST_STD_VER > 17
static_assert(test_one_arg<T>());
static_assert(test_one_arg<const T>());
#endif
}
{
using T = ConstexprTestTypes::TestType;
test_multi_arg<T>();
#if TEST_STD_VER > 17
static_assert(test_multi_arg<T>());
#endif
}
{
using T = ExplicitConstexprTestTypes::TestType;
test_multi_arg<T>();
#if TEST_STD_VER > 17
static_assert(test_multi_arg<T>());
#endif
}
{
using T = TrivialTestTypes::TestType;
test_multi_arg<T>();
#if TEST_STD_VER > 17
static_assert(test_multi_arg<T>());
#endif
}
{
using T = ExplicitTrivialTestTypes::TestType;
test_multi_arg<T>();
#if TEST_STD_VER > 17
static_assert(test_multi_arg<T>());
#endif
}
{
optional<const int> opt;
auto &v = opt.emplace(42);
static_assert( std::is_same_v<const int&, decltype(v)>, "" );
assert(*opt == 42);
assert( v == 42);
opt.emplace();
assert(*opt == 0);
test_empty_emplace();
#if TEST_STD_VER > 17
static_assert(test_empty_emplace());
#endif
}
#ifndef TEST_HAS_NO_EXCEPTIONS
Y::dtor_called = false;

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14
// UNSUPPORTED: gcc-10
// <optional>
// template <class U, class... Args>
@ -25,19 +26,18 @@ class X
{
int i_;
int j_ = 0;
bool* dtor_called_;
public:
static bool dtor_called;
constexpr X() : i_(0) {}
constexpr X(int i) : i_(i) {}
constexpr X(std::initializer_list<int> il) : i_(il.begin()[0]), j_(il.begin()[1]) {}
~X() {dtor_called = true;}
constexpr X(bool& dtor_called) : i_(0), dtor_called_(&dtor_called) {}
constexpr X(int i, bool& dtor_called) : i_(i), dtor_called_(&dtor_called) {}
constexpr X(std::initializer_list<int> il, bool& dtor_called)
: i_(il.begin()[0]), j_(il.begin()[1]), dtor_called_(&dtor_called) {}
TEST_CONSTEXPR_CXX20 ~X() {*dtor_called_ = true;}
friend constexpr bool operator==(const X& x, const X& y)
{return x.i_ == y.i_ && x.j_ == y.j_;}
};
bool X::dtor_called = false;
class Y
{
int i_;
@ -69,17 +69,38 @@ public:
bool Z::dtor_called = false;
TEST_CONSTEXPR_CXX20 bool check_X()
{
bool dtor_called = false;
X x(dtor_called);
optional<X> opt(x);
assert(dtor_called == false);
auto &v = opt.emplace({1, 2}, dtor_called);
static_assert( std::is_same_v<X&, decltype(v)>, "" );
assert(dtor_called);
assert(*opt == X({1, 2}, dtor_called));
assert(&v == &*opt);
return true;
}
TEST_CONSTEXPR_CXX20 bool check_Y()
{
optional<Y> opt;
auto &v = opt.emplace({1, 2});
static_assert( std::is_same_v<Y&, decltype(v)>, "" );
assert(static_cast<bool>(opt) == true);
assert(*opt == Y({1, 2}));
assert(&v == &*opt);
return true;
}
int main(int, char**)
{
{
X x;
optional<X> opt(x);
assert(X::dtor_called == false);
auto &v = opt.emplace({1, 2});
static_assert( std::is_same_v<X&, decltype(v)>, "" );
assert(X::dtor_called == true);
assert(*opt == X({1, 2}));
assert(&v == &*opt);
check_X();
#if TEST_STD_VER > 17
static_assert(check_X());
#endif
}
{
optional<std::vector<int>> opt;
@ -90,12 +111,10 @@ int main(int, char**)
assert(&v == &*opt);
}
{
optional<Y> opt;
auto &v = opt.emplace({1, 2});
static_assert( std::is_same_v<Y&, decltype(v)>, "" );
assert(static_cast<bool>(opt) == true);
assert(*opt == Y({1, 2}));
assert(&v == &*opt);
check_Y();
#if TEST_STD_VER > 17
static_assert(check_Y());
#endif
}
#ifndef TEST_HAS_NO_EXCEPTIONS
{

View File

@ -22,8 +22,21 @@ using std::optional;
using std::nullopt_t;
using std::nullopt;
int main(int, char**)
TEST_CONSTEXPR_CXX20 bool test()
{
enum class State { inactive, constructed, destroyed };
State state = State::inactive;
struct StateTracker {
TEST_CONSTEXPR_CXX20 StateTracker(State& s)
: state_(&s)
{
s = State::constructed;
}
TEST_CONSTEXPR_CXX20 ~StateTracker() { *state_ = State::destroyed; }
State* state_;
};
{
optional<int> opt;
static_assert(noexcept(opt = nullopt) == true, "");
@ -35,6 +48,29 @@ int main(int, char**)
opt = nullopt;
assert(static_cast<bool>(opt) == false);
}
{
optional<StateTracker> opt;
opt = nullopt;
assert(state == State::inactive);
assert(static_cast<bool>(opt) == false);
}
{
optional<StateTracker> opt(state);
assert(state == State::constructed);
opt = nullopt;
assert(state == State::destroyed);
assert(static_cast<bool>(opt) == false);
}
return true;
}
int main(int, char**)
{
#if TEST_STD_VER > 17
static_assert(test());
#endif
test();
using TT = TestTypes::TestType;
TT::reset();
{

View File

@ -14,9 +14,11 @@
// optional<T>& operator=(optional<U>&& rhs);
#include <optional>
#include <type_traits>
#include <memory>
#include <array>
#include <cassert>
#include <memory>
#include <type_traits>
#include "test_macros.h"
#include "archetypes.h"
@ -201,42 +203,109 @@ void test_ambiguous_assign() {
}
TEST_CONSTEXPR_CXX20 bool test()
{
{
optional<int> opt;
optional<short> opt2;
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == false);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
}
{
optional<int> opt;
optional<short> opt2(short{2});
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
assert(*opt == *opt2);
}
{
optional<int> opt(3);
optional<short> opt2;
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == false);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
}
{
optional<int> opt(3);
optional<short> opt2(short{2});
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
assert(*opt == *opt2);
}
enum class state_t { inactive, constructed, copy_assigned, move_assigned };
class StateTracker {
public:
constexpr StateTracker(state_t& s)
: state_(&s)
{
*state_ = state_t::constructed;
}
StateTracker(StateTracker&&) = default;
StateTracker(StateTracker const&) = default;
constexpr StateTracker& operator=(StateTracker&& other) noexcept
{
*state_ = state_t::inactive;
state_ = other.state_;
*state_ = state_t::move_assigned;
other.state_ = nullptr;
return *this;
}
constexpr StateTracker& operator=(StateTracker const& other) noexcept
{
*state_ = state_t::inactive;
state_ = other.state_;
*state_ = state_t::copy_assigned;
return *this;
}
private:
state_t* state_;
};
{
auto state = std::array{state_t::inactive, state_t::inactive};
auto opt1 = std::optional<StateTracker>(state[0]);
assert(state[0] == state_t::constructed);
auto opt2 = std::optional<StateTracker>(state[1]);
assert(state[1] == state_t::constructed);
opt1 = std::move(opt2);
assert(state[0] == state_t::inactive);
assert(state[1] == state_t::move_assigned);
}
{
auto state = std::array{state_t::inactive, state_t::inactive};
auto opt1 = std::optional<StateTracker>(state[0]);
assert(state[0] == state_t::constructed);
auto opt2 = std::optional<StateTracker>(state[1]);
assert(state[1] == state_t::constructed);
opt1 = opt2;
assert(state[0] == state_t::inactive);
assert(state[1] == state_t::copy_assigned);
}
return true;
}
int main(int, char**)
{
#if TEST_STD_VER > 17
static_assert(test());
#endif
test_with_test_type();
test_ambiguous_assign();
{
optional<int> opt;
optional<short> opt2;
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == false);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
}
{
optional<int> opt;
optional<short> opt2(short{2});
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
assert(*opt == *opt2);
}
{
optional<int> opt(3);
optional<short> opt2;
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == false);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
}
{
optional<int> opt(3);
optional<short> opt2(short{2});
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
assert(*opt == *opt2);
}
test();
{
optional<std::unique_ptr<B>> opt;
optional<std::unique_ptr<D>> other(new D());

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14
// UNSUPPORTED: gcc-10
// <optional>
// template <class U>
@ -21,7 +22,7 @@
using std::optional;
template <class T, class U>
void
TEST_CONSTEXPR_CXX20 void
test(const optional<U>& rhs, bool is_going_to_throw = false)
{
bool rhs_engaged = static_cast<bool>(rhs);
@ -51,17 +52,17 @@ class X
{
int i_;
public:
X(int i) : i_(i) {}
X(const X& x) : i_(x.i_) {}
~X() {i_ = 0;}
friend bool operator==(const X& x, const X& y) {return x.i_ == y.i_;}
constexpr X(int i) : i_(i) {}
constexpr X(const X& x) : i_(x.i_) {}
TEST_CONSTEXPR_CXX20 ~X() {i_ = 0;}
friend constexpr bool operator==(const X& x, const X& y) {return x.i_ == y.i_;}
};
class Y
{
int i_;
public:
Y(int i) : i_(i) {}
constexpr Y(int i) : i_(i) {}
friend constexpr bool operator==(const Y& x, const Y& y) {return x.i_ == y.i_;}
};
@ -74,48 +75,33 @@ class Z
public:
Z(int i) : i_(i) {TEST_THROW(6);}
friend constexpr bool operator==(const Z& x, const Z& y) {return x.i_ == y.i_;}
friend bool operator==(const Z& x, const Z& y) {return x.i_ == y.i_;}
};
template<class T, class U>
constexpr bool test_all()
{
{
optional<U> rhs;
test<T>(rhs);
}
{
optional<U> rhs(U{3});
test<T>(rhs);
}
return true;
}
int main(int, char**)
{
{
typedef short U;
typedef int T;
optional<U> rhs;
test<T>(rhs);
}
{
typedef short U;
typedef int T;
optional<U> rhs(U{3});
test<T>(rhs);
}
{
typedef X T;
typedef int U;
optional<U> rhs;
test<T>(rhs);
}
{
typedef X T;
typedef int U;
optional<U> rhs(U{3});
test<T>(rhs);
}
{
typedef Y T;
typedef int U;
optional<U> rhs;
test<T>(rhs);
}
{
typedef Y T;
typedef int U;
optional<U> rhs(U{3});
test<T>(rhs);
}
test_all<int, short>();
test_all<X, int>();
test_all<Y, int>();
#if TEST_STD_VER > 17
static_assert(test_all<int, short>());
static_assert(test_all<X, int>());
static_assert(test_all<Y, int>());
#endif
{
typedef Z T;
typedef int U;

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14
// UNSUPPORTED: gcc-10
// <optional>
// template <class U>
@ -21,7 +22,7 @@
using std::optional;
template <class T, class U>
void
TEST_CONSTEXPR_CXX20 void
test(const optional<U>& rhs, bool is_going_to_throw = false)
{
static_assert(!(std::is_convertible<const optional<U>&, optional<T>>::value), "");
@ -52,17 +53,17 @@ class X
{
int i_;
public:
explicit X(int i) : i_(i) {}
X(const X& x) : i_(x.i_) {}
~X() {i_ = 0;}
friend bool operator==(const X& x, const X& y) {return x.i_ == y.i_;}
constexpr explicit X(int i) : i_(i) {}
constexpr X(const X& x) : i_(x.i_) {}
TEST_CONSTEXPR_CXX20 ~X() {i_ = 0;}
friend constexpr bool operator==(const X& x, const X& y) {return x.i_ == y.i_;}
};
class Y
{
int i_;
public:
explicit Y(int i) : i_(i) {}
constexpr explicit Y(int i) : i_(i) {}
friend constexpr bool operator==(const Y& x, const Y& y) {return x.i_ == y.i_;}
};
@ -73,38 +74,34 @@ class Z
{
int i_;
public:
explicit Z(int i) : i_(i) { TEST_THROW(6);}
constexpr explicit Z(int i) : i_(i) { TEST_THROW(6);}
friend constexpr bool operator==(const Z& x, const Z& y) {return x.i_ == y.i_;}
};
template<class T, class U>
constexpr bool test_all()
{
{
optional<U> rhs;
test<T>(rhs);
}
{
optional<U> rhs(3);
test<T>(rhs);
}
return true;
}
int main(int, char**)
{
{
typedef X T;
typedef int U;
optional<U> rhs;
test<T>(rhs);
}
{
typedef X T;
typedef int U;
optional<U> rhs(3);
test<T>(rhs);
}
{
typedef Y T;
typedef int U;
optional<U> rhs;
test<T>(rhs);
}
{
typedef Y T;
typedef int U;
optional<U> rhs(3);
test<T>(rhs);
}
test_all<X, int>();
test_all<Y, int>();
#if TEST_STD_VER > 17
static_assert(test_all<X, int>());
static_assert(test_all<Y, int>());
#endif
{
typedef Z T;
typedef int U;

View File

@ -21,8 +21,7 @@
using std::optional;
template <class T, class U>
void
test(optional<U>&& rhs, bool is_going_to_throw = false)
TEST_CONSTEXPR_CXX20 void test(optional<U>&& rhs, bool is_going_to_throw = false)
{
static_assert(!(std::is_convertible<optional<U>&&, optional<T>>::value), "");
bool rhs_engaged = static_cast<bool>(rhs);
@ -48,10 +47,10 @@ class X
{
int i_;
public:
explicit X(int i) : i_(i) {}
X(X&& x) : i_(std::exchange(x.i_, 0)) {}
~X() {i_ = 0;}
friend bool operator==(const X& x, const X& y) {return x.i_ == y.i_;}
constexpr explicit X(int i) : i_(i) {}
constexpr X(X&& x) : i_(std::exchange(x.i_, 0)) {}
TEST_CONSTEXPR_CXX20 ~X() {i_ = 0;}
friend constexpr bool operator==(const X& x, const X& y) {return x.i_ == y.i_;}
};
int count = 0;
@ -62,7 +61,7 @@ public:
explicit Z(int) { TEST_THROW(6); }
};
int main(int, char**)
TEST_CONSTEXPR_CXX20 bool test()
{
{
optional<int> rhs;
@ -72,6 +71,16 @@ int main(int, char**)
optional<int> rhs(3);
test<X>(std::move(rhs));
}
return true;
}
int main(int, char**)
{
#if TEST_STD_VER > 17
static_assert(test());
#endif
test();
{
optional<int> rhs;
test<Z>(std::move(rhs));

View File

@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14
// REQUIRES: c++17
// <optional>
// constexpr optional(const optional<T>&& rhs);

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14
// UNSUPPORTED: gcc-10
// <optional>
// template <class U>
@ -22,7 +23,7 @@
using std::optional;
template <class T, class U>
void
TEST_CONSTEXPR_CXX20 void
test(optional<U>&& rhs, bool is_going_to_throw = false)
{
bool rhs_engaged = static_cast<bool>(rhs);
@ -48,37 +49,39 @@ class X
{
int i_;
public:
X(int i) : i_(i) {}
X(X&& x) : i_(std::exchange(x.i_, 0)) {}
~X() {i_ = 0;}
friend bool operator==(const X& x, const X& y) {return x.i_ == y.i_;}
TEST_CONSTEXPR_CXX20 X(int i) : i_(i) {}
TEST_CONSTEXPR_CXX20 X(X&& x) : i_(std::exchange(x.i_, 0)) {}
TEST_CONSTEXPR_CXX20 ~X() {i_ = 0;}
friend constexpr bool operator==(const X& x, const X& y) {return x.i_ == y.i_;}
};
int count = 0;
struct Z
{
Z(int) { TEST_THROW(6); }
};
int main(int, char**)
template<class T, class U>
TEST_CONSTEXPR_CXX20 bool test_all()
{
{
optional<short> rhs;
test<int>(std::move(rhs));
optional<T> rhs;
test<U>(std::move(rhs));
}
{
optional<short> rhs(short{3});
test<int>(std::move(rhs));
}
{
optional<int> rhs;
test<X>(std::move(rhs));
}
{
optional<int> rhs(3);
test<X>(std::move(rhs));
optional<T> rhs(short{3});
test<U>(std::move(rhs));
}
return true;
}
int main(int, char**)
{
test_all<short, int>();
test_all<int, X>();
#if TEST_STD_VER > 17
static_assert(test_all<short, int>());
static_assert(test_all<int, X>());
#endif
{
optional<int> rhs;
test<Z>(std::move(rhs));

View File

@ -40,25 +40,21 @@ int main(int, char**)
typedef int T;
static_assert(std::is_trivially_destructible<T>::value, "");
static_assert(std::is_trivially_destructible<optional<T>>::value, "");
static_assert(std::is_literal_type<optional<T>>::value, "");
}
{
typedef double T;
static_assert(std::is_trivially_destructible<T>::value, "");
static_assert(std::is_trivially_destructible<optional<T>>::value, "");
static_assert(std::is_literal_type<optional<T>>::value, "");
}
{
typedef PODType T;
static_assert(std::is_trivially_destructible<T>::value, "");
static_assert(std::is_trivially_destructible<optional<T>>::value, "");
static_assert(std::is_literal_type<optional<T>>::value, "");
}
{
typedef X T;
static_assert(!std::is_trivially_destructible<T>::value, "");
static_assert(!std::is_trivially_destructible<optional<T>>::value, "");
static_assert(!std::is_literal_type<optional<T>>::value, "");
{
X x;
optional<X> opt{x};

View File

@ -28,7 +28,7 @@ struct X
bool X::dtor_called = false;
int main(int, char**)
constexpr bool check_reset()
{
{
optional<int> opt;
@ -41,6 +41,15 @@ int main(int, char**)
opt.reset();
assert(static_cast<bool>(opt) == false);
}
return true;
}
int main(int, char**)
{
check_reset();
#if TEST_STD_VER >= 20
static_assert(check_reset());
#endif
{
optional<X> opt;
static_assert(noexcept(opt.reset()) == true, "");

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14
// UNSUPPORTED: gcc-10
// <optional>
// void swap(optional&)
@ -63,57 +64,78 @@ public:
friend void swap(Z&, Z&) {TEST_THROW(6);}
};
class W
{
int i_;
public:
constexpr W(int i) : i_(i) {}
friend constexpr bool operator==(const W& x, const W& y) {return x.i_ == y.i_;}
friend TEST_CONSTEXPR_CXX20 void swap(W& x, W& y) noexcept {std::swap(x.i_, y.i_);}
};
template<class T>
TEST_CONSTEXPR_CXX20 bool check_swap()
{
{
optional<T> opt1;
optional<T> opt2;
static_assert(noexcept(opt1.swap(opt2)) == true);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
}
{
optional<T> opt1(1);
optional<T> opt2;
static_assert(noexcept(opt1.swap(opt2)) == true);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == false);
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
}
{
optional<T> opt1;
optional<T> opt2(2);
static_assert(noexcept(opt1.swap(opt2)) == true, "");
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(static_cast<bool>(opt2) == false);
}
{
optional<T> opt1(1);
optional<T> opt2(2);
static_assert(noexcept(opt1.swap(opt2)) == true, "");
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
}
return true;
}
int main(int, char**)
{
{
optional<int> opt1;
optional<int> opt2;
static_assert(noexcept(opt1.swap(opt2)) == true, "");
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
}
{
optional<int> opt1(1);
optional<int> opt2;
static_assert(noexcept(opt1.swap(opt2)) == true, "");
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == false);
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
}
{
optional<int> opt1;
optional<int> opt2(2);
static_assert(noexcept(opt1.swap(opt2)) == true, "");
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(static_cast<bool>(opt2) == false);
}
{
optional<int> opt1(1);
optional<int> opt2(2);
static_assert(noexcept(opt1.swap(opt2)) == true, "");
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
opt1.swap(opt2);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
}
check_swap<int>();
check_swap<W>();
#if TEST_STD_VER > 17
static_assert(check_swap<int>());
static_assert(check_swap<W>());
#endif
{
optional<X> opt1;
optional<X> opt2;