Fix a few mistakes in option, and make it trivial when possible

This commit is contained in:
2025-01-05 15:25:45 +01:00
parent b53fc9038f
commit 8607772d4f
9 changed files with 179 additions and 216 deletions

View File

@ -16,7 +16,7 @@ class box
ASL_NO_UNIQUE_ADDRESS Allocator m_alloc; ASL_NO_UNIQUE_ADDRESS Allocator m_alloc;
public: public:
explicit constexpr box(niche) explicit constexpr box(niche_t)
requires default_constructible<Allocator> requires default_constructible<Allocator>
: m_ptr{nullptr} : m_ptr{nullptr}
, m_alloc{} , m_alloc{}
@ -78,7 +78,7 @@ public:
return m_ptr; return m_ptr;
} }
constexpr bool operator==(niche) const constexpr bool operator==(niche_t) const
{ {
return m_ptr == nullptr; return m_ptr == nullptr;
} }

View File

@ -1,60 +1,72 @@
#pragma once #pragma once
#include "asl/layout.hpp"
#include "asl/memory.hpp"
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/utility.hpp" #include "asl/utility.hpp"
#include "asl/memory.hpp"
namespace asl namespace asl
{ {
template<is_object T> template<is_object T>
class maybe_uninit union maybe_uninit
{ {
union private:
{ T m_value;
alignas(align_of<T>) char m_storage[size_of<T>];
T m_value;
};
public: public:
constexpr maybe_uninit() {} // NOLINT(*-member-init) constexpr maybe_uninit() requires trivially_default_constructible<T> = default;
constexpr maybe_uninit() requires (!trivially_default_constructible<T>) {} // NOLINT
maybe_uninit(const maybe_uninit&) = delete;
maybe_uninit(maybe_uninit&&) = delete;
maybe_uninit& operator=(const maybe_uninit&) = delete;
maybe_uninit& operator=(maybe_uninit&&) = delete;
constexpr ~maybe_uninit() = default;
constexpr ~maybe_uninit() requires (!trivially_destructible<T>) {}
constexpr void* uninit_ptr() && = delete;
constexpr const void* uninit_ptr() const& { return m_storage; }
constexpr void* uninit_ptr() & { return m_storage; }
// @Safety Pointer must only be accessed when in initialized state.
constexpr T* init_ptr_unsafe() && = delete;
constexpr const T* init_ptr_unsafe() const& { return &m_value; }
constexpr T* init_ptr_unsafe() & { return &m_value; }
// @Safety Reference must only be accessed when in initialized state.
constexpr T&& as_init_unsafe() && { return ASL_MOVE(m_value); }
constexpr const T& as_init_unsafe() const& { return m_value; }
constexpr T& as_init_unsafe() & { return m_value; }
// @Safety Must be called only when in uninitialized state.
template<typename... Args> template<typename... Args>
constexpr void init_unsafe(Args&&... args) & explicit constexpr maybe_uninit(in_place_t, Args&&... args)
requires constructible_from<T, Args&&...>
: m_value{ASL_FWD(args)...}
{}
constexpr maybe_uninit(const maybe_uninit&) requires trivially_copy_constructible<T> = default;
constexpr maybe_uninit(const maybe_uninit&) requires (!trivially_copy_constructible<T>) {} // NOLINT
constexpr maybe_uninit(maybe_uninit&&) requires trivially_move_constructible<T> = default;
constexpr maybe_uninit(maybe_uninit&&) requires (!trivially_move_constructible<T>) {} // NOLINT
constexpr maybe_uninit& operator=(const maybe_uninit&) requires trivially_copy_assignable<T> = default;
constexpr maybe_uninit& operator=(const maybe_uninit&) requires (!trivially_copy_assignable<T>) {}
constexpr maybe_uninit& operator=(maybe_uninit&&) requires trivially_move_assignable<T> = default;
constexpr maybe_uninit& operator=(maybe_uninit&&) requires (!trivially_move_assignable<T>) {}
constexpr ~maybe_uninit() requires trivially_destructible<T> = default;
constexpr ~maybe_uninit() requires (!trivially_destructible<T>) {} // NOLINT
// @Safety Value must not have been initialized yet
template<typename... Args>
constexpr void construct_unsafe(Args&&... args)
requires constructible_from<T, Args&&...>
{ {
construct_at<T>(uninit_ptr(), ASL_FWD(args)...); construct_at<T>(&m_value, ASL_FWD(args)...);
} }
// @Safety Must be called only when in initialized state. // @Safety Value must have been initialized
constexpr void uninit_unsafe() & template<typename U>
constexpr void assign_unsafe(U&& value)
requires assignable_from<T&, U&&>
{ {
destroy(init_ptr_unsafe()); m_value = ASL_FWD(value);
} }
// @Safety Value must have been initialized
constexpr void destroy_unsafe()
{
if constexpr (!trivially_destructible<T>)
{
destroy(&m_value);
}
}
// @Safety Value must have been initialized
constexpr const T& as_init_unsafe() const& { return m_value; }
constexpr T& as_init_unsafe() & { return m_value; }
constexpr T&& as_init_unsafe() && { return ASL_MOVE(m_value); }
}; };
} // namespace asl } // namespace asl

View File

@ -190,7 +190,7 @@ template<> struct _is_integer_helper<uint64_t> : true_type {};
template<typename T> concept is_integer = _is_integer_helper<un_cv_t<T>>::value; template<typename T> concept is_integer = _is_integer_helper<un_cv_t<T>>::value;
template<typename T, typename U> template<typename T, typename U>
concept equality_comparable_with = requires (const un_cvref_t<T>& a, const un_cvref_t<T>& b) concept equality_comparable_with = requires (const un_cvref_t<T>& a, const un_cvref_t<U>& b)
{ {
{ a == b } -> same_as<bool>; { a == b } -> same_as<bool>;
{ b == a } -> same_as<bool>; { b == a } -> same_as<bool>;
@ -200,16 +200,12 @@ concept equality_comparable_with = requires (const un_cvref_t<T>& a, const un_cv
template<typename T> concept equality_comparable = equality_comparable_with<T, T>; template<typename T> concept equality_comparable = equality_comparable_with<T, T>;
struct niche {}; struct niche_t {};
template<typename T> template<typename T>
concept has_niche = constructible_from<T, niche> && concept has_niche = constructible_from<T, niche_t> && equality_comparable_with<T, niche_t>;
requires (const T& value, niche n)
{
{ value == n } -> same_as<bool>;
};
template<typename T> template<typename T>
concept is_niche = same_as<un_cvref_t<T>, niche>; concept is_niche = same_as<un_cvref_t<T>, niche_t>;
} // namespace asl } // namespace asl

View File

@ -22,34 +22,24 @@ namespace option_internal
{ {
template<typename T, typename U> template<typename T, typename U>
concept convertible_from_option = concept not_constructible_from_option =
convertible_from<T, option<U>&> && !constructible_from<T, option<U>&> &&
convertible_from<T, const option<U>&> && !constructible_from<T, const option<U>&> &&
convertible_from<T, option<U>&&> && !constructible_from<T, option<U>&&> &&
convertible_from<T, const option<U>&&>; !constructible_from<T, const option<U>&&>;
template<typename T, typename U> template<typename T, typename U>
concept constructible_from_option = concept not_assignable_from_option =
constructible_from<T, option<U>&> && !assignable_from<T&, option<U>&> &&
constructible_from<T, const option<U>&> && !assignable_from<T&, const option<U>&> &&
constructible_from<T, option<U>&&> && !assignable_from<T&, option<U>&&> &&
constructible_from<T, const option<U>&&>; !assignable_from<T&, const option<U>&&>;
template<typename T, typename U>
concept assignable_from_option =
assignable_from<T&, option<U>&> &&
assignable_from<T&, const option<U>&> &&
assignable_from<T&, option<U>&&> &&
assignable_from<T&, const option<U>&&>;
template<typename T, typename U>
concept convertible_constructible_from_option =
convertible_from_option<T, U> && constructible_from_option<T, U>;
template<typename T, typename U> template<typename T, typename U>
concept convertible_constructible_assignable_from_option = concept not_constructible_assignable_from_option =
convertible_constructible_from_option<T, U> && assignable_from_option<T, U>; not_constructible_from_option<T, U> &&
not_assignable_from_option<T, U>;
} // namespace option_internal } // namespace option_internal
@ -65,31 +55,35 @@ class option
{ {
static constexpr bool kHasNiche = has_niche<T>; static constexpr bool kHasNiche = has_niche<T>;
static constexpr bool kHasInlinePayload = default_constructible<T> || kHasNiche;
using Storage = select_t<kHasInlinePayload, T, maybe_uninit<T>>;
using HasValueMarker = select_t<kHasNiche, empty, bool>; using HasValueMarker = select_t<kHasNiche, empty, bool>;
Storage m_payload; maybe_uninit<T> m_payload{};
ASL_NO_UNIQUE_ADDRESS HasValueMarker m_has_value{}; ASL_NO_UNIQUE_ADDRESS HasValueMarker m_has_value{};
template<is_object U>
friend class option;
template<typename... Args> template<typename... Args>
constexpr void construct(Args&&... args) constexpr void construct(Args&&... args)
{ {
ASL_ASSERT(!has_value()); ASL_ASSERT(!has_value());
if constexpr (kHasInlinePayload) if constexpr (!kHasNiche)
{ {
m_payload = T(ASL_FWD(args)...); m_payload.construct_unsafe(ASL_FWD(args)...);
m_has_value = true;
} }
else else
{ {
m_payload.init_unsafe(ASL_FWD(args)...); if constexpr (move_assignable<T>)
} {
m_payload.assign_unsafe(ASL_MOVE(T{ASL_FWD(args)...}));
if constexpr (!kHasNiche) }
{ else
m_has_value = true; {
m_payload.destroy_unsafe();
m_payload.construct_unsafe(ASL_FWD(args)...);
}
} }
} }
@ -97,119 +91,81 @@ class option
constexpr void assign(U&& arg) constexpr void assign(U&& arg)
{ {
ASL_ASSERT(has_value()); ASL_ASSERT(has_value());
m_payload.assign_unsafe(ASL_FWD(arg));
if constexpr (kHasInlinePayload)
{
m_payload = ASL_FWD(arg);
}
else
{
m_payload.as_init_unsafe() = ASL_FWD(arg);
}
} }
public: public:
using type = T; using type = T;
constexpr option() : option(nullopt) {} constexpr option() : option{nullopt} {}
// NOLINTNEXTLINE(*-explicit-conversions) // NOLINTNEXTLINE(*-explicit-conversions)
constexpr option(nullopt_t) requires (!kHasNiche) && trivially_default_constructible<T> {} constexpr option(nullopt_t) requires (!kHasNiche) {}
// NOLINTNEXTLINE(*-explicit-conversions) // NOLINTNEXTLINE(*-explicit-conversions)
constexpr option(nullopt_t) requires (!kHasNiche) && (!trivially_default_constructible<T>) constexpr option(nullopt_t) requires kHasNiche : m_payload{in_place, niche_t{}} {}
: m_payload{}
{}
// NOLINTNEXTLINE(*-explicit-conversions)
constexpr option(nullopt_t) requires kHasNiche
: m_payload{niche{}}
{}
template<typename U = T> template<typename U = T>
constexpr explicit (!convertible_from<T, U&&>) constexpr explicit (!convertible_from<T, U&&>)
option(U&& value) option(U&& value)
requires ( requires (
kHasNiche && kHasNiche &&
constructible_from<T, U> && constructible_from<T, U&&> &&
!same_as<un_cvref_t<U>, option> && !same_as<un_cvref_t<U>, option>
!is_niche<U>
) )
: m_payload(ASL_FWD(value)) : m_payload{in_place, ASL_FWD(value)}
{} {}
template<typename U = T> template<typename U = T>
constexpr explicit (!convertible_from<T, U&&>) constexpr explicit (!convertible_from<T, U&&>)
option(U&& value) option(U&& value)
requires ( requires (
kHasInlinePayload &&
!kHasNiche && !kHasNiche &&
constructible_from<T, U> && constructible_from<T, U&&> &&
!same_as<un_cvref_t<U>, option> && !is_option<U>
!is_niche<U>
) )
: m_payload(ASL_FWD(value)) : m_payload{in_place, ASL_FWD(value)}
, m_has_value{true} , m_has_value{true}
{} {}
template<typename U = T> constexpr option(const option& other) requires trivially_copy_constructible<T> = default;
constexpr explicit (!convertible_from<T, U&&>) constexpr option(const option& other) requires (!copy_constructible<T>) = delete;
option(U&& value)
requires (
!kHasInlinePayload &&
constructible_from<T, U> &&
!same_as<un_cvref_t<U>, option> &&
!is_niche<U>
)
: option(nullopt)
{
construct(ASL_FWD(value));
}
constexpr option(const option& other) constexpr option(const option& other)
requires copy_constructible<T> && kHasInlinePayload = default; requires copy_constructible<T> && (!trivially_copy_constructible<T>)
: option{nullopt}
constexpr option(const option& other)
requires copy_constructible<T> && (!kHasInlinePayload)
: option(nullopt)
{ {
if (other.has_value()) if (other.has_value())
{ {
construct(other.value()); construct(other.m_payload.as_init_unsafe());
} }
} }
constexpr option(const option& other) constexpr option(option&& other) requires trivially_move_constructible<T> = default;
requires (!copy_constructible<T>) = delete; constexpr option(option&& other) requires (!move_constructible<T>) = delete;
constexpr option(option&& other) constexpr option(option&& other)
requires move_constructible<T> && kHasInlinePayload = default; requires move_constructible<T> && (!trivially_move_constructible<T>)
: option{nullopt}
constexpr option(option&& other)
requires move_constructible<T> && (!kHasInlinePayload)
: option(nullopt)
{ {
if (other.has_value()) if (other.has_value())
{ {
construct(ASL_MOVE(other.value())); construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
} }
} }
constexpr option(option&& other)
requires (!move_constructible<T>) = delete;
template<typename U> template<typename U>
constexpr explicit (!convertible_from<T, const U&>) constexpr explicit (!convertible_from<T, const U&>)
option(const option<U>& other) option(const option<U>& other)
requires ( requires (
constructible_from<T, const U&> && constructible_from<T, const U&> &&
!option_internal::convertible_constructible_from_option<T, U> option_internal::not_constructible_from_option<T, U>
) )
: option(nullopt) : option{nullopt}
{ {
if (other.has_value()) if (other.has_value())
{ {
construct(other.value()); construct(other.m_payload.as_init_unsafe());
} }
} }
@ -218,13 +174,13 @@ public:
option(option<U>&& other) option(option<U>&& other)
requires ( requires (
constructible_from<T, U&&> && constructible_from<T, U&&> &&
!option_internal::convertible_constructible_from_option<T, U> option_internal::not_constructible_from_option<T, U>
) )
: option(nullopt) : option{nullopt}
{ {
if (other.has_value()) if (other.has_value())
{ {
construct(ASL_MOVE(other.value())); construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
} }
} }
@ -237,9 +193,9 @@ public:
template<typename U = T> template<typename U = T>
constexpr option& operator=(U&& value) & constexpr option& operator=(U&& value) &
requires ( requires (
assignable_from<T&, U> && assignable_from<T&, U&&> &&
constructible_from<T, U> && constructible_from<T, U&&> &&
!same_as<un_cvref_t<U>, option> !is_option<U>
) )
{ {
if (has_value()) if (has_value())
@ -255,13 +211,13 @@ public:
} }
constexpr option& operator=(const option& other) & constexpr option& operator=(const option& other) &
requires (!copy_assignable<T> || !copy_constructible<T>) = delete; requires (!copy_assignable<T>) = delete;
constexpr option& operator=(const option& other) & constexpr option& operator=(const option& other) &
requires copy_assignable<T> && copy_constructible<T> && kHasInlinePayload = default; requires trivially_copy_assignable<T> = default;
constexpr option& operator=(const option& other) & constexpr option& operator=(const option& other) &
requires copy_assignable<T> && copy_constructible<T> && (!kHasInlinePayload) requires copy_assignable<T> && (!trivially_copy_constructible<T>)
{ {
if (&other == this) { return *this; } if (&other == this) { return *this; }
@ -285,10 +241,13 @@ public:
} }
constexpr option& operator=(option&& other) & constexpr option& operator=(option&& other) &
requires move_assignable<T> && move_constructible<T> && kHasInlinePayload = default; requires (!move_assignable<T>) = delete;
constexpr option& operator=(option&& other) & constexpr option& operator=(option&& other) &
requires move_assignable<T> && move_constructible<T> && (!kHasInlinePayload) requires trivially_move_assignable<T> = default;
constexpr option& operator=(option&& other) &
requires move_assignable<T> && (!trivially_move_constructible<T>)
{ {
if (&other == this) { return *this; } if (&other == this) { return *this; }
@ -316,18 +275,18 @@ public:
requires ( requires (
constructible_from<T, const U&> && constructible_from<T, const U&> &&
assignable_from<T&, const U&> && assignable_from<T&, const U&> &&
!option_internal::convertible_constructible_assignable_from_option<T, U> option_internal::not_constructible_assignable_from_option<T, U>
) )
{ {
if (other.has_value()) if (other.has_value())
{ {
if (has_value()) if (has_value())
{ {
assign(other.value()); assign(other.m_payload.as_init_unsafe());
} }
else else
{ {
construct(other.value()); construct(other.m_payload.as_init_unsafe());
} }
} }
else if (has_value()) else if (has_value())
@ -341,20 +300,20 @@ public:
template<typename U = T> template<typename U = T>
constexpr option& operator=(option<U>&& other) & constexpr option& operator=(option<U>&& other) &
requires ( requires (
constructible_from<T, U> && constructible_from<T, U&&> &&
assignable_from<T&, U> && assignable_from<T&, U&&> &&
!option_internal::convertible_constructible_assignable_from_option<T, U> option_internal::not_constructible_assignable_from_option<T, U>
) )
{ {
if (other.has_value()) if (other.has_value())
{ {
if (has_value()) if (has_value())
{ {
assign(ASL_MOVE(other).value()); assign(ASL_MOVE(other.m_payload.as_init_unsafe()));
} }
else else
{ {
construct(ASL_MOVE(other).value()); construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
} }
} }
else if (has_value()) else if (has_value())
@ -365,7 +324,7 @@ public:
return *this; return *this;
} }
constexpr ~option() = default; constexpr ~option() requires trivially_destructible<T> = default;
constexpr ~option() requires (!trivially_destructible<T>) constexpr ~option() requires (!trivially_destructible<T>)
{ {
reset(); reset();
@ -379,21 +338,18 @@ public:
{ {
if constexpr (move_assignable<T>) if constexpr (move_assignable<T>)
{ {
m_payload = T(niche{}); m_payload.assign_unsafe(ASL_MOVE(T{niche_t{}}));
} }
else else
{ {
destroy(&m_payload); m_payload.destroy_unsafe();
construct_at<T>(&m_payload, niche{}); m_payload.construct_unsafe(niche_t{});
} }
} }
else else
{ {
m_has_value = false; m_has_value = false;
if constexpr (!kHasInlinePayload) m_payload.destroy_unsafe();
{
m_payload.uninit_unsafe();
}
} }
} }
@ -401,7 +357,7 @@ public:
{ {
if constexpr (kHasNiche) if constexpr (kHasNiche)
{ {
return m_payload != niche{}; return m_payload.as_init_unsafe() != niche_t{};
} }
else else
{ {
@ -413,40 +369,19 @@ public:
constexpr T&& value() && constexpr T&& value() &&
{ {
ASL_ASSERT_RELEASE(has_value()); ASL_ASSERT_RELEASE(has_value());
if constexpr (kHasInlinePayload) return ASL_MOVE(m_payload).as_init_unsafe();
{
return ASL_MOVE(m_payload);
}
else
{
return ASL_MOVE(m_payload).as_init_unsafe();
}
} }
constexpr T& value() & constexpr T& value() &
{ {
ASL_ASSERT_RELEASE(has_value()); ASL_ASSERT_RELEASE(has_value());
if constexpr (kHasInlinePayload) return m_payload.as_init_unsafe();
{
return m_payload;
}
else
{
return m_payload.as_init_unsafe();
}
} }
constexpr const T& value() const& constexpr const T& value() const&
{ {
ASL_ASSERT_RELEASE(has_value()); ASL_ASSERT_RELEASE(has_value());
if constexpr (kHasInlinePayload) return m_payload.as_init_unsafe();
{
return m_payload;
}
else
{
return m_payload.as_init_unsafe();
}
} }
template<typename U> template<typename U>

View File

@ -55,6 +55,8 @@ ASL_TEST(arrow)
ASL_TEST(niche) ASL_TEST(niche)
{ {
static_assert(sizeof(asl::box<int>) == sizeof(asl::option<asl::box<int>>));
asl::option<asl::box<int>> opt; asl::option<asl::box<int>> opt;
ASL_TEST_EXPECT(!opt.has_value()); ASL_TEST_EXPECT(!opt.has_value());
@ -66,7 +68,7 @@ ASL_TEST(niche)
ASL_TEST_EXPECT(!opt.has_value()); ASL_TEST_EXPECT(!opt.has_value());
bool destroyed = false; bool destroyed = false;
asl::option<asl::box<DestructorObserver>> opt2 = asl::make_box<DestructorObserver>(&destroyed); asl::option opt2 = asl::make_box<DestructorObserver>(&destroyed);
ASL_TEST_EXPECT(opt2.has_value()); ASL_TEST_EXPECT(opt2.has_value());
ASL_TEST_EXPECT(!destroyed); ASL_TEST_EXPECT(!destroyed);

View File

@ -5,6 +5,18 @@ static_assert(asl::layout::of<int>() == asl::layout::of<asl::maybe_uninit<int>>(
static_assert(asl::size_of<int> == asl::size_of<asl::maybe_uninit<int>>); static_assert(asl::size_of<int> == asl::size_of<asl::maybe_uninit<int>>);
static_assert(asl::align_of<int> == asl::align_of<asl::maybe_uninit<int>>); static_assert(asl::align_of<int> == asl::align_of<asl::maybe_uninit<int>>);
static_assert(asl::trivially_destructible<asl::maybe_uninit<TrivialType>>); #define TEST_TYPE_PROPERTIES(PRP) \
static_assert(!asl::trivially_destructible<asl::maybe_uninit<WithDestructor>>); static_assert(asl::PRP<asl::maybe_uninit<TrivialType>> == asl::PRP<TrivialType>); \
static_assert(asl::PRP<asl::maybe_uninit<TrivialTypeDefaultValue>> == asl::PRP<TrivialTypeDefaultValue>); \
static_assert(asl::PRP<asl::maybe_uninit<WithDestructor>> == asl::PRP<WithDestructor>); \
static_assert(asl::PRP<asl::maybe_uninit<Copyable>> == asl::PRP<Copyable>); \
static_assert(asl::PRP<asl::maybe_uninit<MoveableOnly>> == asl::PRP<MoveableOnly>); \
static_assert(asl::PRP<asl::maybe_uninit<Pinned>> == asl::PRP<Pinned>);
TEST_TYPE_PROPERTIES(trivially_default_constructible);
TEST_TYPE_PROPERTIES(trivially_copy_constructible);
TEST_TYPE_PROPERTIES(trivially_move_constructible);
TEST_TYPE_PROPERTIES(trivially_copy_assignable);
TEST_TYPE_PROPERTIES(trivially_move_assignable);
TEST_TYPE_PROPERTIES(trivially_destructible);

View File

@ -14,14 +14,14 @@ struct NonZero
ASL_ASSERT(x != 0); ASL_ASSERT(x != 0);
} }
constexpr explicit NonZero(asl::niche) : value(0) {} constexpr explicit NonZero(asl::niche_t) : value(0) {}
constexpr bool operator==(asl::niche) const { return value == 0; } constexpr bool operator==(asl::niche_t) const { return value == 0; }
}; };
static_assert(asl::has_niche<NonZero>); static_assert(asl::has_niche<NonZero>);
static_assert(!asl::has_niche<int>); static_assert(!asl::has_niche<int>);
static_assert(sizeof(asl::option<int>) > sizeof(int)); static_assert(sizeof(asl::option<int>) >= sizeof(int));
static_assert(sizeof(asl::option<NonZero>) == sizeof(NonZero)); static_assert(sizeof(asl::option<NonZero>) == sizeof(NonZero));
static_assert(!asl::is_option<int>); static_assert(!asl::is_option<int>);
@ -31,22 +31,26 @@ static_assert(asl::is_option<const asl::option<int>>);
static_assert(asl::trivially_destructible<asl::option<TrivialType>>); static_assert(asl::trivially_destructible<asl::option<TrivialType>>);
static_assert(!asl::trivially_destructible<asl::option<WithDestructor>>); static_assert(!asl::trivially_destructible<asl::option<WithDestructor>>);
static_assert(asl::copy_constructible<asl::option<int>>); static_assert(asl::trivially_copy_constructible<asl::option<int>>);
static_assert(asl::trivially_copy_constructible<asl::option<NonZero>>);
static_assert(asl::copy_constructible<asl::option<Copyable>>); static_assert(asl::copy_constructible<asl::option<Copyable>>);
static_assert(!asl::copy_constructible<asl::option<MoveableOnly>>); static_assert(!asl::copy_constructible<asl::option<MoveableOnly>>);
static_assert(!asl::copy_constructible<asl::option<Pinned>>); static_assert(!asl::copy_constructible<asl::option<Pinned>>);
static_assert(asl::move_constructible<asl::option<int>>); static_assert(asl::trivially_move_constructible<asl::option<int>>);
static_assert(asl::trivially_move_constructible<asl::option<NonZero>>);
static_assert(asl::move_constructible<asl::option<Copyable>>); static_assert(asl::move_constructible<asl::option<Copyable>>);
static_assert(asl::move_constructible<asl::option<MoveableOnly>>); static_assert(asl::move_constructible<asl::option<MoveableOnly>>);
static_assert(!asl::move_constructible<asl::option<Pinned>>); static_assert(!asl::move_constructible<asl::option<Pinned>>);
static_assert(asl::copy_assignable<asl::option<int>>); static_assert(asl::trivially_copy_assignable<asl::option<int>>);
static_assert(asl::trivially_copy_assignable<asl::option<NonZero>>);
static_assert(asl::copy_assignable<asl::option<Copyable>>); static_assert(asl::copy_assignable<asl::option<Copyable>>);
static_assert(!asl::copy_assignable<asl::option<MoveableOnly>>); static_assert(!asl::copy_assignable<asl::option<MoveableOnly>>);
static_assert(!asl::copy_assignable<asl::option<Pinned>>); static_assert(!asl::copy_assignable<asl::option<Pinned>>);
static_assert(asl::move_assignable<asl::option<int>>); static_assert(asl::trivially_move_assignable<asl::option<int>>);
static_assert(asl::trivially_move_assignable<asl::option<NonZero>>);
static_assert(asl::move_assignable<asl::option<Copyable>>); static_assert(asl::move_assignable<asl::option<Copyable>>);
static_assert(asl::move_assignable<asl::option<MoveableOnly>>); static_assert(asl::move_assignable<asl::option<MoveableOnly>>);
static_assert(!asl::move_assignable<asl::option<Pinned>>); static_assert(!asl::move_assignable<asl::option<Pinned>>);

View File

@ -11,6 +11,9 @@
namespace asl namespace asl
{ {
struct in_place_t {};
static constexpr in_place_t in_place{};
template<moveable T> template<moveable T>
constexpr void swap(T& a, T& b) constexpr void swap(T& a, T& b)
{ {

View File

@ -2,6 +2,5 @@ hashing
hash_set hash_set
hash_map hash_map
logging logging
status
status_or status_or
dynlib dynlib