Implementation notes
template <index_t MemberIndex, typename MemberType, typename... MemberTypes>
template <typename CandidateType>
constexpr variant_union<MemberIndex, MemberType, MemberTypes...>::variant_union(
index<MemberIndex>, CandidateType &&that)
: m_head(std::forward<CandidateType>(that))
template <index_t MemberIndex, typename MemberType, typename... MemberTypes>
template <index_t CandidateIndex, typename CandidateType>
constexpr variant_union<MemberIndex, MemberType, MemberTypes...>::variant_union(
index<CandidateIndex>, CandidateType &&that)
: m_tail(index_v<CandidateIndex>, std::forward<CandidateType>(that)) {}
template <index_t MemberIndex, typename MemberType, typename... MemberTypes>
template <index_t CandidateIndex, typename CandidateType>
constexpr decltype(auto)
variant_union<MemberIndex, MemberType, MemberTypes...>::
operator=(forwarding_ntuple<index<CandidateIndex>, CandidateType> that) {
if constexpr (CandidateIndex > MemberIndex)
m_tail = that;
else
m_head = std::forward<CandidateType>(that.get(index_v<1>));
template <typename ThatType> struct variant_match {
-
template <typename MemberType>
using exact =
std::is_same<std::conditional_t<std::is_reference_v<MemberType>,
MemberType, remove_cvref_t<MemberType>>,
std::conditional_t<std::is_reference_v<MemberType>, ThatType,
remove_cvref_t<ThatType>>>;
-
Type filter to check whether the member type is exactly the same as
another.
If a member type is a reference, then we really are saying
we must only have references, so we must compare exactly with references
intact. If the member type is not a reference, then the user supplying an
object may not explicitly state that that is a reference and so an
exact match with references stripped is okay.
In the future, allow for a const
member
type that underneath is not const
because const
members are annoying.
This is kind of like argument binding, but for templates.
-
template <typename MemberType>
using construct = std::is_constructible<MemberType, ThatType>;
-
Type filter to check whether member type is constructible from
another.
Don't need to do any clever reference matching stuff, since the
trait already covers it.
This is kind of like argument binding, but for templates.
-
template <typename MemberType>
using assign = std::is_assignable<remove_cvref_t<MemberType> &, ThatType>;
-
Type filter to check whether member type is assignable from
another.
The member type may not be a reference, but if it is the active member
object at the time of assignment, then it is a reference because it
is named, so we must check types based on that reality.
This is kind of like argument binding, but for templates.
-
template <typename ThatType>
template <typename... MemberTypes>
constexpr auto variant_match<ThatType>::match_construct() {
constexpr type_list list = make_type_list<MemberTypes...>();
constexpr index exact_index = index_v<match_indices<exact>(list)[index_v<0>]>;
[[maybe_unused]] constexpr index construct_index =
index_v<match_indices<construct>(list)[index_v<0>]>;
if constexpr (exact_index > -1)
return exact_index;
else if constexpr (construct_index > -1)
return construct_index;
template <typename ThatType>
template <typename... MemberTypes>
constexpr auto variant_match<ThatType>::match_assign() {
constexpr type_list list = make_type_list<MemberTypes...>();
constexpr index exact_index = index_v<match_indices<exact>(list)[index_v<0>]>;
[[maybe_unused]] constexpr index assign_index =
index_v<match_indices<assign>(list)[index_v<0>]>;
if constexpr (exact_index > -1)
return exact_index;
else if constexpr (assign_index > -1)
template <typename... MemberTypes>
template <typename CandidateType, typename>
constexpr variant<MemberTypes...>::variant(CandidateType &&that)
: variant(variant_match<CandidateType>::template match_construct<
MemberTypes...>(),
std::forward<CandidateType>(that)) {}
template <typename... MemberTypes>
template <typename CandidateType, typename>
constexpr decltype(auto) variant<MemberTypes...>::
operator=(CandidateType &&that) {
variant::operator=
({variant_match<CandidateType>::template match_assign<MemberTypes...>(),
std::forward<CandidateType>(that)});
template <typename... MemberTypes>
template <index_t CandidateIndex, typename CandidateType>
constexpr decltype(auto) variant<MemberTypes...>::
operator=(forwarding_ntuple<gut::index<CandidateIndex>, CandidateType> that) {
if constexpr (std::is_trivially_copy_assignable_v<union_type> ||
std::is_trivially_move_assignable_v<union_type>)
m_union = union_type{that.get(index_v<0>),
std::forward<CandidateType>(that.get(index_v<1>))};
else if constexpr (std::is_assignable_v<
decltype(get_type<CandidateIndex>(
make_index_list<MemberTypes...>()))::raw_type,
CandidateType>) {
if (index() == CandidateIndex)
m_union = that;
else
needsReset = true;
} else
needsReset = true;
if (needsReset) {
reset();
new (std::addressof(m_union))
union_type{index_v<CandidateIndex>,
std::forward<CandidateType>(that.get(index_v<1>))};
template <typename... MemberTypes>
template <typename MemberType>
constexpr decltype(auto)
variant<MemberTypes...>::value(type<MemberType>) const {
template <typename... MemberTypes>
template <typename MemberType>
constexpr decltype(auto) variant<MemberTypes...>::value(type<MemberType>) {
using return_type =
std::conditional_t<std::is_reference_v<MemberType>, MemberType,
remove_cvref_t<MemberType> &>;
return reduce(*this, [](auto &&v) -> return_type {
if constexpr (std::is_convertible_v<decltype(v), return_type>)
return static_cast<return_type>(v);
else
return optional<return_type>{}.value();
template <typename... MemberTypes>
template <index_t MemberIndex>
constexpr decltype(auto)
variant<MemberTypes...>::get(gut::index<MemberIndex>) const {
template <typename... MemberTypes>
template <index_t MemberIndex>
constexpr decltype(auto) variant<MemberTypes...>::get(gut::index<MemberIndex>) {
if constexpr (MemberIndex == 0)
return index();
template <typename... MemberTypes>
constexpr void variant<MemberTypes...>::reset() {
make_index_list<count()>().reduce([this](auto... i) mutable constexpr {
(..., [ this, i ]() mutable constexpr {
if (index() == i)
m_union.reset(i);
}());
template <typename... MemberTypes>
template <typename Variant, typename Map>
constexpr decltype(auto) variant<MemberTypes...>::map(Variant &&v, Map &&m) {
using result_type = variant<return_type_t<Map, decltype(v.value(i))>...>;
optional<result_type> result;
(..., [&v, &m, &result, i ]() mutable constexpr->decltype(auto) {
if (v.index() == i)
result = {i, call(m, v.value(i))};
}());
template <typename... MemberTypes>
template <typename Variant, typename Reduce>
constexpr decltype(auto) variant<MemberTypes...>::reduce(Variant &&v,
Reduce &&r) {
using result_type =
std::common_type_t<return_type_t<Reduce, decltype(v.value(i))>...>;
optional<result_type> result;
v.each([&result, &r ](auto &&v) mutable constexpr { result = call(r, v); });