Skip to content

Instantly share code, notes, and snippets.

@seiren-naru-shirayuri
Last active November 20, 2023 08:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seiren-naru-shirayuri/d0493137d688eb91277d00e0ed48b9a2 to your computer and use it in GitHub Desktop.
Save seiren-naru-shirayuri/d0493137d688eb91277d00e0ed48b9a2 to your computer and use it in GitHub Desktop.
A "flat" C++ tuple implementation with multiple inheritance in C++20 syntax.

A "flat" C++ tuple implementation with multiple inheritance in C++20 syntax.

This gist is released under GLWTPL.

Tested on MSVC 19.32 with /std:c++20, GCC 12.1 with -std=c++20, clang 13 (clang-cl) with /std:c++20.

Difference from std::tuple and std::pair:

  1. All names except partial specialization of class templates in std inhabit in global namespace instead of std namespace.
  2. pair does not contain member object first and second, but contains two member functions first() and second(), which return the reference to first and second object (for lvalue pair) or first and second object (for rvalue pair) respectively.
  3. To pass std::array to tuple_cat, call qualified ::tuple_cat instead of unqualified tuple_cat, because calling unqualified tuple_cat may cause ADL to find std::tuple_cat with some compilers.
// Example
#include <iostream>
#include <array>
#include "tuple.hpp"
template <std::size_t ...Is, typename ...Ts>
void print_tuple_helper(const tuple<Ts...>& t, std::index_sequence<Is...>)
{
using dummy = int[];
static_cast<void>(dummy{ 0, ((std::cout << get<Is>(t) << ' '), 0)... });
std::cout << std::endl;
}
template <typename ...Ts>
void print_tuple(const tuple<Ts...>& t)
{
print_tuple_helper(t, std::make_index_sequence<sizeof...(Ts)>());
}
auto prints = [](auto&& ...ts)
{
using dummy = int[];
static_cast<void>(dummy{ 0, ((std::cout << ts << ' '), 0)... });
std::cout << std::endl;
};
int main()
{
static constexpr auto t{ tuple(1, 2.5f, 'a', "abc", 2) };
static constexpr auto t2{ tuple(2, "def", 7.5, 'd') };
print_tuple(t);
std::remove_const_t<decltype(t)> t11{}, t12{};
t11 = t;
print_tuple(t11);
swap(t11, t12);
print_tuple(t12);
constexpr auto t3 = tuple_cat(t, t2);
print_tuple(t3);
constexpr std::array a{ 1, 9, 2, 8, 3, 7 };
constexpr auto t4 = ::tuple_cat(a, t3);
print_tuple(t4);
int i; double d; char c;
tie(i, ignore, d, c) = t2;
prints(i, d, c);
auto&& [i2, ignore, d2, c2] = t2;
prints(i2, d2, c2);
constexpr auto&& v1 = get<0>(t);
// constexpr auto&& v1 = get<5>(t) // Error: Index out of bounds.
constexpr auto&& v2 = get<float>(t);
// constexpr auto&& v2 = get<int>(t); // Error: Type not unique.
// constexpr auto&& v2 = get<long long>(t); // Error: Type not exist.
// constexpr auto&& v3 = get<int>(t2); // Error: Empty tuple.
std::cout << v1 << '\n' << v2 << std::endl;
static constexpr auto t5{ tuple(2.0f, 7.5, 999, "defg", (short)4) };
if (t < t5)
{
std::cout << "t is less than t2." << std::endl;
}
apply(prints, t2);
}
#pragma once
#ifndef PAIR_HPP_INCLUDED
#define PAIR_HPP_INCLUDED
#include "tuple.hpp"
// std::tuple_size<pair> metafunction
template <typename T, typename U>
struct std::tuple_size<pair<T, U>> : std::integral_constant<std::size_t, 2> {};
// std::tuple_element<pair> metafunction
template <std::size_t I, typename T, typename U>
struct std::tuple_element<I, pair<T, U>>
{
static_assert(I >= 2, "Index of out bounds.");
using type = std::conditional_t<I == 0, T, U>;
};
// std::basic_common_reference<pair> metafunction
template <typename T1, typename U1, typename T2, typename U2, template <typename> class TT1, template <typename> class TT2>
requires requires { typename pair<std::common_reference_t<TT1<T1>, TT2<T2>>, std::common_reference_t<TT1<U1>, TT2<U2>>>; }
struct std::basic_common_reference<pair<T1, U1>, pair<T2, U2>, TT1, TT2>
{
using type = pair<std::common_reference_t<TT1<T1>, TT2<T2>>, std::common_reference_t<TT1<U1>, TT2<U2>>>;
};
// std::common_type<pair> metafunction
template <typename T1, typename U1, typename T2, typename U2>
requires requires { typename pair<std::common_type_t<T1, T2>, std::common_type_t<U1, U2>>; }
struct std::common_type<pair<T1, U1>, pair<T2, U2>>
{
using type = pair<std::common_type_t<T1, T2>, std::common_type_t<U1, U2>>;
};
// make_pair function
template <typename T, typename U>
constexpr auto make_pair(T&& t, U&& u)
{
return pair<std::unwrap_ref_decay_t<T>, std::unwrap_ref_decay_t<U>>(std::forward<T>(t), std::forward<U>(u));
}
// get function
template <std::size_t I, typename T, typename U>
constexpr std::tuple_element_t<I, pair<T, U>>& get(pair<T, U>& p) noexcept
{
return static_cast<tuple_impl::tuple_unit<I, std::tuple_element_t<I, pair<T, U>>>&>(p).get();
}
template <std::size_t I, typename T, typename U>
constexpr const std::tuple_element_t<I, pair<T, U>>& get(const pair<T, U>& p) noexcept
{
return static_cast<const tuple_impl::tuple_unit<I, std::tuple_element_t<I, pair<T, U>>>&>(p).get();
}
template <std::size_t I, typename T, typename U>
constexpr std::tuple_element_t<I, pair<T, U>>&& get(pair<T, U>&& p) noexcept
{
return std::move(static_cast<tuple_impl::tuple_unit<I, std::tuple_element_t<I, pair<T, U>>>&&>(p).get());
}
template <std::size_t I, typename T, typename U>
constexpr const std::tuple_element_t<I, pair<T, U>>&& get(const pair<T, U>&& p) noexcept
{
return std::move(static_cast<const tuple_impl::tuple_unit<I, std::tuple_element_t<I, pair<T, U>>>&&>(p).get());
}
template <typename T, typename U>
constexpr T& get(pair<T, U>& p) noexcept
{
return static_cast<tuple_impl::tuple_unit<0, T>&>(p).get();
}
template <typename T, typename U>
constexpr const T& get(const pair<T, U>& p) noexcept
{
return static_cast<const tuple_impl::tuple_unit<0, T>&>(p).get();
}
template <typename T, typename U>
constexpr T&& get(pair<T, U>&& p) noexcept
{
return std::move(static_cast<tuple_impl::tuple_unit<0, T>&&>(p).get());
}
template <typename T, typename U>
constexpr const T&& get(const pair<T, U>&& p) noexcept
{
return std::move(static_cast<const tuple_impl::tuple_unit<0, T>&&>(p).get());
}
template <typename T, typename U>
constexpr T& get(pair<U, T>& p) noexcept
{
return static_cast<tuple_impl::tuple_unit<1, T>&>(p).get();
}
template <typename T, typename U>
constexpr const T& get(const pair<U, T>& p) noexcept
{
return static_cast<const tuple_impl::tuple_unit<1, T>&>(p).get();
}
template <typename T, typename U>
constexpr T&& get(pair<U, T>&& p) noexcept
{
return std::move(static_cast<tuple_impl::tuple_unit<1, T>&&>(p).get());
}
template <typename T, typename U>
constexpr const T&& get(const pair<U, T>&& p) noexcept
{
return std::move(static_cast<const tuple_impl::tuple_unit<1, T>&&>(p).get());
}
// operator== and operator<=> functions
template <typename T, typename U>
constexpr bool operator==(const pair<T, U>& lhs, const pair<T, U>& rhs)
{
return static_cast<const tuple_impl::tuple_base<std::index_sequence<0, 1>, T, U>&>(lhs).equal(static_cast<const tuple_impl::tuple_base<std::index_sequence<0, 1>, T, U>&>(rhs));
}
template <typename T, typename U>
constexpr auto operator<=>(const pair<T, U>& lhs, const pair<T, U>& rhs)
requires (std::three_way_comparable<T> && std::three_way_comparable<U>) || (tuple_impl::Comparable<T> && tuple_impl::Comparable<U>)
{
return static_cast<const tuple_impl::tuple_base<std::index_sequence<0, 1>, T, U>&>(lhs).three_way_compare(static_cast<const tuple_impl::tuple_base<std::index_sequence<0, 1>, T, U>&>(rhs));
}
// swap function
template <typename T, typename U>
constexpr void swap(pair<T, U>& lhs, pair<T, U>& rhs) noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
template <typename T, typename U>
constexpr void swap(const pair<T, U>& lhs, const pair<T, U>& rhs) noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
#endif // PAIR_HPP_INCLUDED
#pragma once
#ifndef TUPLE_HPP_INCLUDED
#define TUPLE_HPP_INCLUDED
#ifndef TUPLE_BASE
#define TUPLE_BASE
#endif // !TUPLE_BASE
#include "tuple_base.hpp"
#ifdef TUPLE_BASE
#undef TUPLE_BASE
#endif // TUPLE_BASE
// pair class template forward declaration
template <typename, typename>
class pair;
// tuple class templates
template <typename ...Ts>
class tuple final : public tuple_impl::tuple_base<std::make_index_sequence<sizeof...(Ts)>, Ts...>
{
public:
using indices = TYPENAME tuple_impl::tuple_base<std::make_index_sequence<sizeof...(Ts)>, Ts...>::indices;
using types = TYPENAME tuple_impl::tuple_base<std::make_index_sequence<sizeof...(Ts)>, Ts...>::types;
// MSVC fold expression bug workaround (fixed in MSVC 19.32)
#if defined(_MSC_VER) && _MSC_VER < 1932
explicit(std::negation_v<std::conjunction<std::bool_constant<tuple_impl::ImplicitlyDefaultConstructible<Ts>>...>>)
#else
explicit(!(... && tuple_impl::ImplicitlyDefaultConstructible<Ts>))
#endif
constexpr tuple()
requires std::conjunction_v<std::is_default_constructible<Ts>...>
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<0>{}) {}
explicit(std::negation_v<std::conjunction<std::is_convertible<const Ts&, Ts>...>>)
constexpr tuple(const Ts& ...ts)
requires (sizeof...(Ts) >= 1) && std::conjunction_v<std::is_copy_constructible<Ts>...>
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<1>{}, ts...) {}
template <typename ...Us>
explicit(std::negation_v<std::conjunction<std::is_convertible<Us, Ts>...>>)
constexpr tuple(Us&& ...us)
requires (sizeof...(Ts) == sizeof...(Us)) && (sizeof...(Ts) >= 1) && std::conjunction_v<std::is_constructible<Ts, Us>...> && (((sizeof...(Ts) == 1) && std::negation_v<std::is_same<std::remove_cvref_t<tuple_impl::get_first_type_t<Us...>>, tuple>>) || (((sizeof...(Ts) == 2) || (sizeof...(Ts) == 3)) && ((!std::is_same_v<std::remove_cvref_t<tuple_impl::get_first_type_t<Us...>>, std::allocator_arg_t>) || std::is_same_v<std::remove_cvref_t<tuple_impl::get_first_type_t<Ts...>>, std::allocator_arg_t>)) || (sizeof...(Ts) > 3))
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<1>{}, std::forward<Us>(us)...) {}
template <typename ...Us>
explicit(std::negation_v<std::conjunction<std::is_convertible<Us&, Ts>...>>)
constexpr tuple(tuple<Us...>& other)
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_constructible<Ts, Us&>...> && ((sizeof...(Ts) != 1) || std::negation_v<std::disjunction<std::is_convertible<tuple<tuple_impl::get_first_type_t<Us...>>&, tuple_impl::get_first_type_t<Ts...>>, std::is_constructible<tuple_impl::get_first_type_t<Ts...>, tuple<tuple_impl::get_first_type_t<Us...>>&>, std::is_same<tuple_impl::get_first_type_t<Ts...>, tuple_impl::get_first_type_t<Us...>>>>)
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<tuple_impl::tuple_base<indices, Us...>&>(other), tuple_impl::type_pack<Us...>{}) {}
template <typename ...Us>
explicit(std::negation_v<std::conjunction<std::is_convertible<const Us&, Ts>...>>)
constexpr tuple(const tuple<Us...>& other)
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_constructible<Ts, const Us&>...> && ((sizeof...(Ts) != 1) || std::negation_v<std::disjunction<std::is_convertible<const tuple<tuple_impl::get_first_type_t<Us...>>&, tuple_impl::get_first_type_t<Ts...>>, std::is_constructible<tuple_impl::get_first_type_t<Ts...>, const tuple<tuple_impl::get_first_type_t<Us...>>&>, std::is_same<tuple_impl::get_first_type_t<Ts...>, tuple_impl::get_first_type_t<Us...>>>>)
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<const tuple_impl::tuple_base<indices, Us...>&>(other), tuple_impl::type_pack<Us...>{}) {}
template <typename ...Us>
explicit(std::negation_v<std::conjunction<std::is_convertible<Us, Ts>...>>)
constexpr tuple(tuple<Us...>&& other)
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_constructible<Ts, Us>...> && ((sizeof...(Ts) != 1) || std::negation_v<std::disjunction<std::is_convertible<tuple<tuple_impl::get_first_type_t<Us...>>, tuple_impl::get_first_type_t<Ts...>>, std::is_constructible<tuple_impl::get_first_type_t<Ts...>, tuple<tuple_impl::get_first_type_t<Us...>>>, std::is_same<tuple_impl::get_first_type_t<Ts...>, tuple_impl::get_first_type_t<Us...>>>>)
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<tuple_impl::tuple_base<indices, Us...>&&>(other), tuple_impl::type_pack<Us...>{}) {}
template <typename ...Us>
explicit(std::negation_v<std::conjunction<std::is_convertible<const Us, Ts>...>>)
constexpr tuple(const tuple<Us...>&& other)
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_constructible<Ts, const Us>...> && ((sizeof...(Ts) != 1) || std::negation_v<std::disjunction<std::is_convertible<const tuple<tuple_impl::get_first_type_t<Us...>>, tuple_impl::get_first_type_t<Ts...>>, std::is_constructible<tuple_impl::get_first_type_t<Ts...>, const tuple<tuple_impl::get_first_type_t<Us...>>>, std::is_same<tuple_impl::get_first_type_t<Ts...>, tuple_impl::get_first_type_t<Us...>>>>)
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<const tuple_impl::tuple_base<indices, Us...>&&>(other), tuple_impl::type_pack<Us...>{}) {}
template <typename U, typename V>
explicit(std::negation_v<std::conjunction<std::is_convertible<U&, tuple_impl::get_first_type_t<Ts...>>, std::is_convertible<V&, tuple_impl::get_second_type_t<Ts...>>>>)
constexpr tuple(pair<U, V>& other)
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_constructible<tuple_impl::get_first_type_t<Ts...>, U&>, std::is_constructible<tuple_impl::get_second_type_t<Ts...>, V&>>
: tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&>(other), tuple_impl::type_pack<U, V>{}) {}
template <typename U, typename V>
explicit(std::negation_v<std::conjunction<std::is_convertible<const U&, tuple_impl::get_first_type_t<Ts...>>, std::is_convertible<const V&, tuple_impl::get_second_type_t<Ts...>>>>)
constexpr tuple(const pair<U, V>& other)
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_constructible<tuple_impl::get_first_type_t<Ts...>, const U&>, std::is_constructible<tuple_impl::get_second_type_t<Ts...>, const V&>>
: tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<const tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&>(other), tuple_impl::type_pack<U, V>{}) {}
template <typename U, typename V>
explicit(std::negation_v<std::conjunction<std::is_convertible<U, tuple_impl::get_first_type_t<Ts...>>, std::is_convertible<V, tuple_impl::get_second_type_t<Ts...>>>>)
constexpr tuple(pair<U, V>&& other)
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_constructible<tuple_impl::get_first_type_t<Ts...>, U>, std::is_constructible<tuple_impl::get_second_type_t<Ts...>, V>>
: tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&&>(other), tuple_impl::type_pack<U, V>{}) {}
template <typename U, typename V>
explicit(std::negation_v<std::conjunction<std::is_convertible<const U, tuple_impl::get_first_type_t<Ts...>>, std::is_convertible<const V, tuple_impl::get_second_type_t<Ts...>>>>)
constexpr tuple(const pair<U, V>&& other)
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_constructible<tuple_impl::get_first_type_t<Ts...>, const U>, std::is_constructible<tuple_impl::get_second_type_t<Ts...>, const V>>
: tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<const tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&&>(other), tuple_impl::type_pack<U, V>{}) {}
template <std::size_t ...Is>
explicit constexpr tuple(tuple_impl::tuple_base<std::index_sequence<Is...>, Ts...>&& other) : tuple_impl::tuple_base<std::index_sequence<Is...>, Ts...>(other) {}
tuple(const tuple& other) = default;
tuple(tuple&& other) = default;
// MSVC fold expression bug workaround (fixed in MSVC 19.32)
template <typename A>
#if defined(_MSC_VER) && _MSC_VER < 1932
explicit(std::negation_v<std::conjunction<std::bool_constant<tuple_impl::ImplicitlyDefaultConstructible<Ts>>...>>)
#else
explicit(!(... && tuple_impl::ImplicitlyDefaultConstructible<Ts>))
#endif
constexpr tuple(std::allocator_arg_t, const A& a)
requires std::conjunction_v<std::is_default_constructible<Ts>...>
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<5>{}, a) {}
template <typename A>
explicit(std::negation_v<std::conjunction<std::is_convertible<const Ts&, Ts>...>>)
constexpr tuple(std::allocator_arg_t, const A& a, const Ts& ...ts)
requires (sizeof...(Ts) >= 1) && std::conjunction_v<std::is_copy_constructible<Ts>...>
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<6>{}, a, ts...) {}
template <typename A, typename ...Us>
explicit(std::negation_v<std::conjunction<std::is_convertible<Us, Ts>...>>)
constexpr tuple(std::allocator_arg_t, const A& a, Us&& ...us)
requires (sizeof...(Ts) == sizeof...(Us)) && (sizeof...(Ts) >= 1) && std::conjunction_v<std::is_constructible<Ts, Us>...> && (((sizeof...(Ts) == 1) && std::negation_v<std::is_same<std::remove_cvref_t<tuple_impl::get_first_type_t<Us...>>, tuple>>) || (((sizeof...(Ts) == 2) || (sizeof...(Ts) == 3)) && ((!std::is_same_v<std::remove_cvref_t<tuple_impl::get_first_type_t<Us...>>, std::allocator_arg_t>) || std::is_same_v<std::remove_cvref_t<tuple_impl::get_first_type_t<Ts...>>, std::allocator_arg_t>)) || (sizeof...(Ts) > 3))
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<6>{}, a, std::forward<Us>(us)...) {}
template <typename A, typename ...Us>
explicit(std::negation_v<std::conjunction<std::is_convertible<Us&, Ts>...>>)
constexpr tuple(std::allocator_arg_t, const A& a, tuple<Us...>& other)
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_constructible<Ts, Us&>...> && ((sizeof...(Ts) != 1) || std::negation_v<std::disjunction<std::is_convertible<tuple<tuple_impl::get_first_type_t<Us...>>&, tuple_impl::get_first_type_t<Ts...>>, std::is_constructible<tuple_impl::get_first_type_t<Ts...>, tuple<tuple_impl::get_first_type_t<Us...>>&>, std::is_same<tuple_impl::get_first_type_t<Ts...>, tuple_impl::get_first_type_t<Us...>>>>)
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<7>{}, a, static_cast<tuple_impl::tuple_base<indices, Ts...>&>(other), tuple_impl::type_pack<Us...>{}) {}
template <typename A, typename ...Us>
explicit(std::negation_v<std::conjunction<std::is_convertible<const Us, Ts>...>>)
constexpr tuple(std::allocator_arg_t, const A& a, const tuple<Us...>& other)
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_constructible<Ts, const Us>...> && ((sizeof...(Ts) != 1) || std::negation_v<std::disjunction<std::is_convertible<const tuple<tuple_impl::get_first_type_t<Us...>>, tuple_impl::get_first_type_t<Ts...>>, std::is_constructible<tuple_impl::get_first_type_t<Ts...>, const tuple<tuple_impl::get_first_type_t<Us...>>>, std::is_same<tuple_impl::get_first_type_t<Ts...>, tuple_impl::get_first_type_t<Us...>>>>)
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<7>{}, a, static_cast<const tuple_impl::tuple_base<indices, Ts...>&>(other), tuple_impl::type_pack<Us...>{}) {}
template <typename A, typename ...Us>
explicit(std::negation_v<std::conjunction<std::is_convertible<Us, Ts>...>>)
constexpr tuple(std::allocator_arg_t, const A& a, tuple<Us...>&& other)
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_constructible<Ts, Us>...> && ((sizeof...(Ts) != 1) || std::negation_v<std::disjunction<std::is_convertible<tuple<tuple_impl::get_first_type_t<Us...>>, tuple_impl::get_first_type_t<Ts...>>, std::is_constructible<tuple_impl::get_first_type_t<Ts...>, tuple<tuple_impl::get_first_type_t<Us...>>>, std::is_same<tuple_impl::get_first_type_t<Ts...>, tuple_impl::get_first_type_t<Us...>>>>)
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<7>{}, a, static_cast<tuple_impl::tuple_base<indices, Ts...>&&>(other), tuple_impl::type_pack<Us...>{}) {}
template <typename A, typename ...Us>
explicit(std::negation_v<std::conjunction<std::is_convertible<const Us, Ts>...>>)
constexpr tuple(std::allocator_arg_t, const A& a, const tuple<Us...>&& other)
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_constructible<Ts, const Us>...> && ((sizeof...(Ts) != 1) || std::negation_v<std::disjunction<std::is_convertible<const tuple<tuple_impl::get_first_type_t<Us...>>, tuple_impl::get_first_type_t<Ts...>>, std::is_constructible<tuple_impl::get_first_type_t<Ts...>, const tuple<tuple_impl::get_first_type_t<Us...>>>, std::is_same<tuple_impl::get_first_type_t<Ts...>, tuple_impl::get_first_type_t<Us...>>>>)
: tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<7>{}, a, static_cast<const tuple_impl::tuple_base<indices, Ts...>&&>(other), tuple_impl::type_pack<Us...>{}) {}
template <typename A, typename U, typename V>
explicit(std::negation_v<std::conjunction<std::is_convertible<U&, tuple_impl::get_first_type_t<Ts...>>, std::is_convertible<V&, tuple_impl::get_second_type_t<Ts...>>>>)
constexpr tuple(std::allocator_arg_t, const A& a, pair<U, V>& other)
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_constructible<tuple_impl::get_first_type_t<Ts...>, U&>, std::is_constructible<tuple_impl::get_second_type_t<Ts...>, V&>>
: tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>(tuple_impl::tuple_base_constructor_tag<7>{}, a, static_cast<tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&>(other), tuple_impl::type_pack<U, V>{}) {}
template <typename A, typename U, typename V>
explicit(std::negation_v<std::conjunction<std::is_convertible<const U&, tuple_impl::get_first_type_t<Ts...>>, std::is_convertible<const V&, tuple_impl::get_second_type_t<Ts...>>>>)
constexpr tuple(std::allocator_arg_t, const A& a, const pair<U, V>& other)
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_constructible<tuple_impl::get_first_type_t<Ts...>, const U&>, std::is_constructible<tuple_impl::get_second_type_t<Ts...>, const V&>>
: tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>(tuple_impl::tuple_base_constructor_tag<7>{}, a, static_cast<const tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&>(other), tuple_impl::type_pack<U, V>{}) {}
template <typename A, typename U, typename V>
explicit(std::negation_v<std::conjunction<std::is_convertible<U, tuple_impl::get_first_type_t<Ts...>>, std::is_convertible<V, tuple_impl::get_second_type_t<Ts...>>>>)
constexpr tuple(std::allocator_arg_t, const A& a, pair<U, V>&& other)
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_constructible<tuple_impl::get_first_type_t<Ts...>, U>, std::is_constructible<tuple_impl::get_second_type_t<Ts...>, V>>
: tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>(tuple_impl::tuple_base_constructor_tag<7>{}, a, static_cast<tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&&>(other), tuple_impl::type_pack<U, V>{}) {}
template <typename A, typename U, typename V>
explicit(std::negation_v<std::conjunction<std::is_convertible<const U, tuple_impl::get_first_type_t<Ts...>>, std::is_convertible<const V, tuple_impl::get_second_type_t<Ts...>>>>)
constexpr tuple(std::allocator_arg_t, const A& a, const pair<U, V>&& other)
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_constructible<tuple_impl::get_first_type_t<Ts...>, const U>, std::is_constructible<tuple_impl::get_second_type_t<Ts...>, const V>>
: tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>(tuple_impl::tuple_base_constructor_tag<7>{}, a, static_cast<const tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&&>(other), tuple_impl::type_pack<U, V>{}) {}
template <typename A>
tuple(std::allocator_arg_t, const A& a, const tuple& other) : tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<5>{}, a, static_cast<const tuple_impl::tuple_base<indices, Ts...>&>(other), tuple_impl::type_pack<Ts...>{}) {}
template <typename A>
tuple(std::allocator_arg_t, const A& a, tuple&& other) : tuple_impl::tuple_base<indices, Ts...>(tuple_impl::tuple_base_constructor_tag<5>{}, a, static_cast<tuple_impl::tuple_base<indices, Ts...>&&>(other), tuple_impl::type_pack<Ts...>{}) {}
constexpr tuple& operator=(const tuple& other)
requires std::conjunction_v<std::is_copy_assignable<Ts>...>
{
this->assign(tuple_impl::tuple_base_assign_tag<0>{}, static_cast<const tuple_impl::tuple_base<indices, Ts...>&>(other));
return *this;
}
constexpr tuple& operator=(const tuple& other) = delete;
constexpr const tuple& operator=(const tuple& other) const
requires std::conjunction_v<std::is_copy_assignable<const Ts>...>
{
this->assign(tuple_impl::tuple_base_assign_tag<1>{}, static_cast<const tuple_impl::tuple_base<indices, Ts...>&>(other));
return *this;
}
constexpr tuple& operator=(tuple&& other)
noexcept(std::conjunction_v<std::is_nothrow_move_assignable<Ts>...>)
requires std::conjunction_v<std::is_move_assignable<Ts>...>
{
this->assign(tuple_impl::tuple_base_assign_tag<2>{}, static_cast<tuple_impl::tuple_base<indices, Ts...>&&>(other));
return *this;
}
constexpr const tuple& operator=(tuple&& other) const
requires std::conjunction_v<std::is_assignable<const Ts&, Ts>...>
{
this->assign(tuple_impl::tuple_base_assign_tag<3>{}, static_cast<tuple_impl::tuple_base<indices, Ts...>&&>(other));
return *this;
}
template <typename ...Us>
constexpr tuple& operator=(const tuple<Us...>& other)
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_assignable<Ts&, const Us&>...>
{
this->assign(tuple_impl::tuple_base_assign_tag<0>{}, static_cast<const tuple_impl::tuple_base<indices, Us...>&>(other));
return *this;
}
template <typename ...Us>
constexpr const tuple& operator=(const tuple<Us...>& other) const
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_assignable<const Ts&, const Us&>...>
{
this->assign(tuple_impl::tuple_base_assign_tag<1>{}, static_cast<const tuple_impl::tuple_base<indices, Us...>&>(other));
return *this;
}
template <typename ...Us>
constexpr tuple& operator=(tuple<Us...>&& other)
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_assignable<Ts&, Us>...>
{
this->assign(tuple_impl::tuple_base_assign_tag<2>{}, static_cast<tuple_impl::tuple_base<indices, Ts...>&&>(other));
return *this;
}
template <typename ...Us>
constexpr const tuple& operator=(tuple<Us...>&& other) const
requires (sizeof...(Ts) == sizeof...(Us)) && std::conjunction_v<std::is_assignable<const Ts&, Us>...>
{
this->assign(tuple_impl::tuple_base_assign_tag<3>{}, static_cast<tuple_impl::tuple_base<indices, Ts...>&&>(other));
return *this;
}
template <typename U, typename V>
constexpr tuple& operator=(const pair<U, V>& other)
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_assignable<std::add_lvalue_reference_t<tuple_impl::get_first_type_t<Ts...>>, const U&>, std::is_assignable<std::add_lvalue_reference_t<tuple_impl::get_second_type_t<Ts...>>, const V&>>
{
this->assign(tuple_impl::tuple_base_assign_tag<0>{}, static_cast<const tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&>(other));
return *this;
}
template <typename U, typename V>
constexpr const tuple& operator=(const pair<U, V>& other) const
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_assignable<std::add_lvalue_reference_t<tuple_impl::get_first_type_t<Ts...>>, const U&>, std::is_assignable<std::add_lvalue_reference_t<tuple_impl::get_second_type_t<Ts...>>, const V&>>
{
this->assign(tuple_impl::tuple_base_assign_tag<1>{}, static_cast<const tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&>(other));
return *this;
}
template <typename U, typename V>
constexpr tuple& operator=(pair<U, V>&& other)
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_assignable<std::add_const_t<std::add_lvalue_reference_t<tuple_impl::get_first_type_t<Ts...>>>, const U&>, std::is_assignable<std::add_const_t<std::add_lvalue_reference_t<tuple_impl::get_second_type_t<Ts...>>>, const V&>>
{
this->assign(tuple_impl::tuple_base_assign_tag<2>{}, static_cast<tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&&>(other));
return *this;
}
template <typename U, typename V>
constexpr const tuple& operator=(pair<U, V>&& other) const
requires (sizeof...(Ts) == 2) && std::conjunction_v<std::is_assignable<std::add_lvalue_reference_t<tuple_impl::get_first_type_t<Ts...>>, U>, std::is_assignable<std::add_lvalue_reference_t<tuple_impl::get_second_type_t<Ts...>>, V>>
{
this->assign(tuple_impl::tuple_base_assign_tag<3>{}, static_cast<tuple_impl::tuple_base<std::index_sequence<0, 1>, U, V>&&>(other));
return *this;
}
constexpr void swap(tuple& other) noexcept(std::conjunction_v<std::is_nothrow_swappable<Ts>...>)
{
static_cast<tuple_impl::tuple_base<indices, Ts...>&>(*this).swap(static_cast<tuple_impl::tuple_base<indices, Ts...>&>(other));
}
constexpr void swap(const tuple& other) const noexcept(std::conjunction_v<std::is_nothrow_swappable<const Ts>...>)
{
static_cast<const tuple_impl::tuple_base<indices, Ts...>&>(*this).swap(static_cast<const tuple_impl::tuple_base<indices, Ts...>&>(other));
}
template <typename ...Ts2, typename ...Us2>
friend constexpr bool operator==(const tuple<Ts2...>& lhs, const tuple<Us2...>& rhs)
requires (sizeof...(Ts2) == sizeof...(Us2));
template <typename ...Ts2, typename ...Us2>
friend constexpr auto operator<=>(const tuple<Ts2...>& lhs, const tuple<Us2...>& rhs)
requires (sizeof...(Ts2) == sizeof...(Us2)) && ((... && std::three_way_comparable_with<Ts2, Us2>) || (... && tuple_impl::ComparableWith<Ts2, Us2>));
template <typename ...Ts2>
friend constexpr void swap(tuple<Ts2...>& lhs, tuple<Ts2...>& rhs) noexcept(noexcept(lhs.swap(rhs)));
template <typename ...Ts2>
friend constexpr void swap(const tuple<Ts2...>& lhs, const tuple<Ts2...>& rhs) noexcept(noexcept(lhs.swap(rhs)));
};
template <>
struct tuple<> final
{
constexpr tuple() = default;
constexpr tuple(tuple&) = default;
constexpr tuple(const tuple&) = default;
constexpr tuple(tuple&&) noexcept = default;
constexpr tuple(const tuple&&) {}
template <typename A>
constexpr tuple(std::allocator_arg_t, const A&) {}
template <typename A, typename ...Us>
constexpr tuple(std::allocator_arg_t, const A&, tuple&) {}
template <typename A, typename ...Us>
constexpr tuple(std::allocator_arg_t, const A&, const tuple&) {}
template <typename A, typename ...Us>
constexpr tuple(std::allocator_arg_t, const A&, tuple&&) {}
template <typename A, typename ...Us>
constexpr tuple(std::allocator_arg_t, const A&, const tuple&&) {}
constexpr tuple& operator=(const tuple&)
{
return *this;
}
constexpr const tuple& operator=(const tuple&) const
{
return *this;
}
constexpr tuple& operator=(tuple&&)
{
return *this;
}
constexpr const tuple& operator=(tuple&&) const
{
return *this;
}
constexpr void swap(tuple&) noexcept {}
constexpr void swap(const tuple&) const noexcept {}
friend constexpr bool operator==(const tuple<>& lhs, const tuple<>& rhs);
friend constexpr auto operator<=>(const tuple<>& lhs, const tuple<>& rhs);
friend constexpr void swap(tuple&, tuple&) noexcept;
friend constexpr void swap(const tuple&, const tuple&) noexcept;
};
template <typename ...Ts>
tuple(Ts...) -> tuple<Ts...>;
template <typename T, typename U>
tuple(pair<T, U>) -> tuple<T, U>;
template <typename A, typename ...Ts>
tuple(std::allocator_arg_t, A, Ts...) -> tuple<Ts...>;
template <typename A, typename T, typename U>
tuple(std::allocator_arg_t, A, pair<T, U>) -> tuple<T, U>;
template <typename A, typename ...Ts>
tuple(std::allocator_arg_t, A, tuple<Ts...>) -> tuple<Ts...>;
template <std::size_t ...Is, typename ...Ts>
tuple(tuple_impl::tuple_base<std::index_sequence<Is...>, Ts...>) -> tuple<Ts...>;
// pair class template
template <typename T, typename U>
class pair final : public tuple_impl::tuple_base<std::index_sequence<0, 1>, T, U>
{
public:
using indices = std::index_sequence<0, 1>;
using first_type = T;
using second_type = U;
explicit(!(tuple_impl::ImplicitlyDefaultConstructible<T>&& tuple_impl::ImplicitlyDefaultConstructible<U>))
constexpr pair()
requires std::conjunction_v<std::is_default_constructible<T>, std::is_default_constructible<U>>
: tuple_impl::tuple_base<indices, T, U>(tuple_impl::tuple_base_constructor_tag<0>{}) {}
explicit(std::negation_v<std::conjunction<std::is_convertible<const T&, T>, std::is_convertible<const U&, U>>>)
constexpr pair(const T& t, const U& u)
requires std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_constructible<U>>
: tuple_impl::tuple_base<indices, T, U>(tuple_impl::tuple_base_constructor_tag<1>{}, t, u) {}
template <typename T2 = T, typename U2 = U>
explicit(std::negation_v<std::conjunction<std::is_convertible<T2, T>, std::is_convertible<U2, U>>>)
constexpr pair(T2&& t, U2&& u)
requires std::conjunction_v<std::is_constructible<T, T2>, std::is_constructible<U, U2>>
: tuple_impl::tuple_base<indices, T, U>(tuple_impl::tuple_base_constructor_tag<1>{}, std::forward<T2>(t), std::forward<U2>(u)) {}
template <typename T2, typename U2>
explicit(std::negation_v<std::conjunction<std::is_convertible<T2&, T>, std::is_convertible<U2&, U>>>)
constexpr pair(pair<T2, U2>& other)
requires std::conjunction_v<std::is_constructible<T, T2&>, std::is_constructible<U, U2&>>
: tuple_impl::tuple_base<indices, T, U>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<tuple_impl::tuple_base<indices, T2, U2>&>(other), tuple_impl::type_pack<T2, U2>{}) {}
template <typename T2, typename U2>
explicit(std::negation_v<std::conjunction<std::is_convertible<const T2&, T>, std::is_convertible<const U2&, U>>>)
constexpr pair(const pair<T2, U2>& other)
requires std::conjunction_v<std::is_constructible<T, const T2&>, std::is_constructible<U, const U2&>>
: tuple_impl::tuple_base<indices, T, U>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<const tuple_impl::tuple_base<indices, T2, U2>&>(other), tuple_impl::type_pack<T2, U2>{}) {}
template <typename T2, typename U2>
explicit(std::negation_v<std::conjunction<std::is_convertible<T2, T>, std::is_convertible<U2, U>>>)
constexpr pair(pair<T2, U2>&& other)
requires std::conjunction_v<std::is_constructible<T, T2>, std::is_constructible<U, U2>>
: tuple_impl::tuple_base<indices, T, U>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<tuple_impl::tuple_base<indices, T2, U2>&&>(other), tuple_impl::type_pack<T2, U2>{}) {}
template <typename T2, typename U2>
explicit(std::negation_v<std::conjunction<std::is_convertible<const T2, T>, std::is_convertible<const U2, U>>>)
constexpr pair(const pair<T2, U2>&& other)
requires std::conjunction_v<std::is_constructible<T, const T2>, std::is_constructible<U, const U2>>
: tuple_impl::tuple_base<indices, T, U>(tuple_impl::tuple_base_constructor_tag<2>{}, static_cast<const tuple_impl::tuple_base<indices, T2, U2>&&>(other), tuple_impl::type_pack<T2, U2>{}) {}
template <typename ...Ts, typename ...Us>
constexpr pair(std::piecewise_construct_t tag, tuple<Ts...> t, tuple<Us...> u) : tuple_impl::tuple_base<indices, T, U>(tuple_impl::tuple_base_constructor_tag<8>{}, static_cast<tuple_impl::tuple_base<typename tuple<Ts...>::indices, Ts...>&>(t), static_cast<tuple_impl::tuple_base<typename tuple<Us...>::indices, Us...>&>(u)) {}
pair(const pair& other) = default;
pair(pair&& other) = default;
constexpr pair& operator=(const pair& other)
requires std::conjunction_v<std::is_copy_assignable<T>, std::is_copy_assignable<U>>
{
this->assign(tuple_impl::tuple_base_assign_tag<0>{}, static_cast<const tuple_impl::tuple_base<indices, T, U>&>(other));
return *this;
}
constexpr pair& operator=(const pair& other) = delete;
constexpr const pair& operator=(const pair& other) const
requires std::conjunction_v<std::is_copy_assignable<const T>, std::is_copy_assignable<const U>>
{
this->assign(tuple_impl::tuple_base_assign_tag<1>{}, static_cast<const tuple_impl::tuple_base<indices, T, U>&>(other));
return *this;
}
constexpr pair&& operator=(pair&& other)
noexcept(std::conjunction_v<std::is_nothrow_move_assignable<T>, std::is_nothrow_move_assignable<U>>)
requires std::conjunction_v<std::is_move_assignable<T>, std::is_move_assignable<U>>
{
this->assign(tuple_impl::tuple_base_assign_tag<2>{}, static_cast<tuple_impl::tuple_base<indices, T, U>&&>(other));
return *this;
}
constexpr const pair&& operator=(pair&& other) const
requires std::conjunction_v<std::is_assignable<const T&, T>, std::is_assignable<const U&, U>>
{
this->assign(tuple_impl::tuple_base_assign_tag<3>{}, static_cast<tuple_impl::tuple_base<indices, T, U>&&>(other));
return *this;
}
template <typename T2, typename U2>
constexpr pair& operator=(const pair<T2, U2>& other)
requires std::conjunction_v<std::is_assignable<T&, const T2&>, std::is_assignable<U&, const U2&>>
{
this->assign(tuple_impl::tuple_base_assign_tag<0>{}, static_cast<const tuple_impl::tuple_base<indices, T2, U2>&>(other));
return *this;
}
template <typename T2, typename U2>
constexpr const pair& operator=(const pair<T2, U2>& other) const
requires std::conjunction_v<std::is_assignable<const T&, const T2&>, std::is_assignable<const U&, const U2&>>
{
this->assign(tuple_impl::tuple_base_assign_tag<1>{}, static_cast<const tuple_impl::tuple_base<indices, T2, U2>&>(other));
return *this;
}
template <typename T2, typename U2>
constexpr pair& operator=(pair<T2, U2>&& other)
requires std::conjunction_v<std::is_assignable<T&, T2>, std::is_assignable<U&, U2>>
{
this->assign(tuple_impl::tuple_base_assign_tag<2>{}, static_cast<tuple_impl::tuple_base<indices, T2, U2>&&>(other));
return *this;
}
template <typename T2, typename U2>
constexpr const pair& operator=(pair<T2, U2>&& other) const
requires std::conjunction_v<std::is_assignable<const T&, T2>, std::is_assignable<const U&, U2>>
{
this->assign(tuple_impl::tuple_base_assign_tag<3>{}, static_cast<tuple_impl::tuple_base<indices, T2, U2>&&>(other));
return *this;
}
constexpr void swap(pair& other) noexcept(std::conjunction_v<std::is_nothrow_swappable<T>, std::is_nothrow_swappable<U>>)
{
static_cast<tuple_impl::tuple_base<indices, T, U>&>(*this).swap(static_cast<tuple_impl::tuple_base<indices, T, U>&>(other));
}
constexpr void swap(const pair& other) const noexcept(std::conjunction_v<std::is_nothrow_swappable<const T>, std::is_nothrow_swappable<const U>>)
{
static_cast<const tuple_impl::tuple_base<indices, T, U>&>(*this).swap(static_cast<const tuple_impl::tuple_base<indices, T, U>&>(other));
}
constexpr T& first() &
{
return static_cast<tuple_impl::tuple_unit<0, T>&>(*this).get();
}
constexpr const T& first() const&
{
return static_cast<const tuple_impl::tuple_unit<0, T>&>(*this).get();
}
constexpr T first() &&
{
return std::move(static_cast<tuple_impl::tuple_unit<0, T>&&>(*this).get());
}
constexpr const T first() const&&
{
return std::move(static_cast<const tuple_impl::tuple_unit<0, T>&&>(*this).get());
}
constexpr U& second() &
{
return static_cast<tuple_impl::tuple_unit<1, U>&>(*this).get();
}
constexpr const U& second() const&
{
return static_cast<const tuple_impl::tuple_unit<1, U>&>(*this).get();
}
constexpr U second() &&
{
return std::move(static_cast<tuple_impl::tuple_unit<1, U>&&>(*this).get());
}
constexpr const U second() const&&
{
return std::move(static_cast<const tuple_impl::tuple_unit<1, U>&&>(*this).get());
}
template <typename T2, typename U2>
friend constexpr bool operator==(const pair<T2, U2>& lhs, const pair<T2, U2>& rhs);
template <typename T2, typename U2>
friend constexpr auto operator<=>(const pair<T2, U2>& lhs, const pair<T2, U2>& rhs)
requires (std::three_way_comparable<T2>&& std::three_way_comparable<U2>) || (tuple_impl::Comparable<T2> && tuple_impl::Comparable<U2>);
template <typename T2, typename U2>
friend constexpr void swap(pair<T2, U2>& lhs, pair<T2, U2>& rhs) noexcept(noexcept(lhs.swap(rhs)));
template <typename T2, typename U2>
friend constexpr void swap(const pair<T2, U2>& lhs, const pair<T2, U2>& rhs) noexcept(noexcept(lhs.swap(rhs)));
};
template <typename T, typename U>
pair(T, U) -> pair<T, U>;
// std::tuple_size<tuple> metafunction
template <typename ...Ts>
struct std::tuple_size<tuple<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> {};
// std::tuple_element<tuple> metafunction
template <std::size_t I, typename T, typename ...Us>
struct std::tuple_element<I, tuple<T, Us...>>
{
static_assert(I < sizeof...(Us) + 1, "Index out of bounds.");
using type = TYPENAME tuple_element<I - 1, tuple<Us...>>::type;
};
template <typename T, typename ...Us>
struct std::tuple_element<0, tuple<T, Us...>>
{
using type = T;
};
// std::uses_allocator<tuple> trait
template <typename ...Ts, typename A>
struct std::uses_allocator<tuple<Ts...>, A> : std::true_type {};
// std::basic_common_reference<tuple> metafunction
template <typename ...Ts, typename ...Us, template <typename> class TT1, template <typename> class TT2>
requires requires { typename tuple<std::common_reference_t<TT1<Ts>, TT2<Us>>...>; }
struct std::basic_common_reference<tuple<Ts...>, tuple<Us...>, TT1, TT2>
{
using type = tuple<std::common_reference_t<TT1<Ts>, TT2<Us>>...>;
};
// std::common_type<tuple> metafunction
template <typename ...Ts, typename ...Us>
requires requires { typename tuple<std::common_type_t<Ts, Us>...>; }
struct std::common_type<tuple<Ts...>, tuple<Us...>>
{
using type = tuple<std::common_type_t<Ts, Us>...>;
};
// ignore object;
struct ignore_t
{
template <typename T>
constexpr void operator=(T&&) const {}
};
inline constexpr ignore_t ignore;
// make_tuple function
template <typename ...Ts>
constexpr auto make_tuple(Ts&& ...ts)
{
return tuple<std::unwrap_ref_decay_t<Ts>...>(std::forward<Ts>(ts)...);
}
// tie function
template <typename ...Ts>
constexpr tuple<Ts&...> tie(Ts& ...ts) noexcept
{
return { ts... };
}
// forward_as_tuple function
template <typename ...Ts>
constexpr tuple<Ts&&...> forward_as_tuple(Ts&& ...ts) noexcept
{
return tuple<Ts&&...>(std::forward<Ts>(ts)...);
}
// tuple_cat function
namespace tuple_impl
{
template <typename>
struct is_tuple : std::false_type {};
template <typename ...Ts>
struct is_tuple<tuple<Ts...>> : std::true_type {};
template <typename T>
inline constexpr bool is_tuple_v = is_tuple<T>::value;
template <typename>
struct is_pair : std::false_type {};
template <typename T, typename U>
struct is_pair<pair<T, U>> : std::true_type {};
template <typename T>
inline constexpr bool is_pair_v = is_pair<T>::value;
template <typename T, std::size_t ...Is, typename ...Ts>
constexpr decltype(auto) forward_to_tuple_base(T&& t, std::index_sequence<Is...>, type_pack<Ts...>)
{
return forward_to<tuple_base<std::index_sequence<Is...>, Ts...>>(t);
}
template <typename T>
constexpr decltype(auto) try_forward_to_tuple_base(T&& t)
{
if constexpr (is_tuple_v<std::remove_cvref_t<T>>)
{
return forward_to_tuple_base(std::forward<T>(t), typename std::remove_cvref_t<T>::indices{}, typename std::remove_cvref_t<T>::types{});
}
else if constexpr (is_pair_v<std::remove_cvref_t<T>>)
{
return forward_to_tuple_base(std::forward<T>(t), typename std::remove_cvref_t<T>::indices{}, type_pack<typename std::remove_cvref_t<T>::first_type, typename std::remove_cvref_t<T>::second_type>{});
}
else
{
static_assert(is_array_v<std::remove_cvref_t<T>>, "tuple_cat only accepts tuple, pair and std::array.");
return t;
}
}
template <typename ...Ts>
struct tuple_cat_result_helper
{
static_assert(always_false_v<Ts...>, "tuple_cat only accepts tuple, pair and std::array.");
};
template <typename ...Ts>
struct tuple_cat_result_helper<tuple<Ts...>>
{
using type = tuple<Ts...>;
};
template <template <typename ...> class TT1, typename ...Ts1, template <typename ...> class TT2, typename ...Ts2, typename ...Us>
struct tuple_cat_result_helper<TT1<Ts1...>, TT2<Ts2...>, Us...>
{
using type = TYPENAME tuple_cat_result_helper<tuple<Ts1..., Ts2...>, Us...>::type;
};
template <template <typename ...> class TT, typename ...Ts, typename T, std::size_t N, typename ...Us>
struct tuple_cat_result_helper<TT<Ts...>, std::array<T, N>, Us...>
{
using type = TYPENAME tuple_cat_result_helper<tuple<Ts...>, make_n_types_t<T, N>, Us...>::type;
};
template <typename T, std::size_t N, template <typename ...> class TT, typename ...Ts, typename ...Us>
struct tuple_cat_result_helper<std::array<T, N>, TT<Ts...>, Us...>
{
using type = TYPENAME tuple_cat_result_helper<make_n_types_t<T, N>, tuple<Ts...>, Us...>::type;
};
template <typename T1, std::size_t N1, typename T2, std::size_t N2, typename ...Us>
struct tuple_cat_result_helper<std::array<T1, N1>, std::array<T2, N2>, Us...>
{
using type = TYPENAME tuple_cat_result_helper<make_n_types_t<T1, N1>, make_n_types_t<T2, N2>, Us...>::type;
};
template <typename ...Ts>
struct tuple_cat_result
{
using type = TYPENAME tuple_cat_result_helper<std::remove_cvref_t<Ts>...>::type;
};
template <typename ...Ts>
using tuple_cat_result_t = TYPENAME tuple_cat_result<Ts...>::type;
}
template <typename ...Ts>
constexpr tuple_impl::tuple_cat_result_t<Ts...> tuple_cat(Ts&& ...ts)
{
return tuple(tuple_impl::tuple_cat_impl(tuple_impl::try_forward_to_tuple_base(ts)...));
}
// get function
namespace tuple_impl
{
template <template <std::size_t, typename ...> class TT, std::size_t I, typename ...Ts>
struct defer
{
using type = TT<I, Ts...>;
};
template <std::size_t, typename, typename>
struct tuple_index_helper;
template <std::size_t I, typename T, typename U, typename ...Vs>
struct tuple_index_helper<I, T, tuple<U, Vs...>>
{
static_assert(!std::is_same_v<T, U>, "Type not unique.");
static constexpr std::size_t index = tuple_index_helper<I, T, tuple<Vs...>>::index;
};
template <std::size_t I, typename T>
struct tuple_index_helper<I, T, tuple<>>
{
static constexpr std::size_t index = I;
};
template <std::size_t, typename, typename>
struct tuple_index;
template <std::size_t I, typename T>
struct tuple_index<I, T, tuple<>>
{
static_assert(!(I == 0), "Empty tuple.");
static_assert(!(I != 0), "Type not exist.");
};
template <std::size_t I, typename T, typename U, typename ...Vs>
struct tuple_index<I, T, tuple<U, Vs...>>
{
static constexpr std::size_t index = std::conditional_t<std::is_same_v<T, U>, defer<tuple_index_helper, I, T, tuple<Vs...>>, defer<tuple_index, I + 1, T, tuple<Vs...>>>::type::index;
};
template <typename T, typename U>
inline constexpr std::size_t tuple_index_v = tuple_index<0, T, U>::index;
}
template <std::size_t I, typename ...Ts>
constexpr std::tuple_element_t<I, tuple<Ts...>>& get(tuple<Ts...>& t) noexcept
{
return static_cast<tuple_impl::tuple_unit<I, typename std::tuple_element_t<I, tuple<Ts...>>>&>(t).get();
}
template <std::size_t I, typename ...Ts>
constexpr const std::tuple_element_t<I, tuple<Ts...>>& get(const tuple<Ts...>& t) noexcept
{
return static_cast<const tuple_impl::tuple_unit<I, typename std::tuple_element_t<I, tuple<Ts...>>>&>(t).get();
}
template <std::size_t I, typename ...Ts>
constexpr std::tuple_element_t<I, tuple<Ts...>>&& get(tuple<Ts...>&& t) noexcept
{
return std::move(static_cast<tuple_impl::tuple_unit<I, typename std::tuple_element_t<I, tuple<Ts...>>>&&>(t).get());
}
template <std::size_t I, typename ...Ts>
constexpr const std::tuple_element_t<I, tuple<Ts...>>&& get(const tuple<Ts...>&& t) noexcept
{
return std::move(static_cast<const tuple_impl::tuple_unit<I, typename std::tuple_element_t<I, tuple<Ts...>>>&&>(t).get());
}
template <typename T, typename ...Us>
constexpr T& get(tuple<Us...>& t) noexcept
{
return static_cast<tuple_impl::tuple_unit<tuple_impl::tuple_index_v<T, tuple<Us...>>, T>&>(t).get();
}
template <typename T, typename ...Us>
constexpr const T& get(const tuple<Us...>& t) noexcept
{
return static_cast<const tuple_impl::tuple_unit<tuple_impl::tuple_index_v<T, tuple<Us...>>, T>&>(t).get();
}
template <typename T, typename ...Us>
constexpr T&& get(tuple<Us...>&& t) noexcept
{
return std::move(static_cast<tuple_impl::tuple_unit<tuple_impl::tuple_index_v<T, tuple<Us...>>, T>&&>(t).get());
}
template <typename T, typename ...Us>
constexpr const T&& get(const tuple<Us...>&& t) noexcept
{
return std::move(static_cast<const tuple_impl::tuple_unit<tuple_impl::tuple_index_v<T, tuple<Us...>>, T>&&>(t).get());
}
// operator== and operator<=> functions
template <typename ...Ts, typename ...Us>
constexpr bool operator==(const tuple<Ts...>& lhs, const tuple<Us...>& rhs)
requires (sizeof...(Ts) == sizeof...(Us))
{
return static_cast<const tuple_impl::tuple_base<typename tuple<Ts...>::indices, Ts...>&>(lhs).equal(static_cast<const tuple_impl::tuple_base<typename tuple<Us...>::indices, Us...>&>(rhs));
}
template <typename ...Ts, typename ...Us>
constexpr auto operator<=>(const tuple<Ts...>& lhs, const tuple<Us...>& rhs)
requires (sizeof...(Ts) == sizeof...(Us)) && ((... && std::three_way_comparable_with<Ts, Us>) || (... && tuple_impl::ComparableWith<Ts, Us>))
{
return static_cast<const tuple_impl::tuple_base<typename tuple<Ts...>::indices, Ts...>&>(lhs).three_way_compare(static_cast<const tuple_impl::tuple_base<typename tuple<Us...>::indices, Us...>&>(rhs));
}
template <>
constexpr bool operator==(const tuple<>&, const tuple<>&)
{
return true;
}
template <>
constexpr auto operator<=>(const tuple<>&, const tuple<>&)
{
return std::strong_ordering::equal;
}
// swap function
template <typename ...Ts>
constexpr void swap(tuple<Ts...>& lhs, tuple<Ts...>& rhs) noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
template <typename ...Ts>
constexpr void swap(const tuple<Ts...>& lhs, const tuple<Ts...>& rhs) noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
template <>
constexpr void swap(tuple<>&, tuple<>&) noexcept {}
template <>
constexpr void swap(const tuple<>&, const tuple<>&) noexcept {}
// apply function
template <typename F, typename T>
constexpr decltype(auto) apply(F&& f, T&& t)
{
return tuple_impl::apply_impl(std::forward<F>(f), tuple_impl::try_forward_to_tuple_base(t));
}
// make_from_tuple function
namespace tuple_impl
{
template <typename R, typename T, std::size_t ...Is, typename ...Ts>
constexpr R make_from_tuple_impl(T&& t, std::index_sequence<Is...>, type_pack<Ts...>)
{
return T(forward_to<tuple_impl::tuple_unit<Is, Ts>>(t).get()...);
}
}
template <typename R, typename T>
constexpr R make_from_tuple(T&& t)
{
return tuple_impl::make_from_tuple_impl(std::forward<T>(t), typename std::remove_cvref_t<T>::indices{}, typename std::remove_cvref_t<T>::types{});
}
#endif // TUPLE_HPP_INCLUDED
#pragma once
#ifndef TUPLE_BASE_HPP_INCLUDED
#define TUPLE_BASE_HPP_INCLUDED
#ifndef TUPLE_BASE
#error Do not include this file directly, include "tuple.hpp" or "pair.hpp" instead.
#endif
#include <utility>
#include <cstddef>
#include <type_traits>
#include <memory>
#include <compare>
#include <functional>
// Workaround for clang not implementing P0634R3
#ifdef __clang__
#define TYPENAME typename
#else
#define TYPENAME
#endif // __clang__
// Enable empty base optimization for MSVC
#if defined(_MSC_VER) && _MSC_VER >= 1900
#define EMPTY_BASES __declspec(empty_bases)
#else
#define EMPTY_BASES
#endif
namespace tuple_impl
{
template <typename ...>
struct always_false : std::false_type {};
template <typename ...Ts>
inline constexpr bool always_false_v = always_false<Ts...>::value;
template <std::size_t I>
struct tuple_unit_constructor_tag {};
template <std::size_t I>
struct tuple_unit_assign_tag {};
template <std::size_t I>
struct tuple_base_constructor_tag {};
template <std::size_t I>
struct tuple_base_assign_tag {};
template <typename T, typename U>
struct copy_cvref
{
private:
using T2 = std::remove_cvref_t<T>;
using U2 = std::remove_reference_t<U>;
using T3 = std::conditional_t<std::is_const_v<U2>, std::add_const_t<T2>, T2>;
using T4 = std::conditional_t<std::is_volatile_v<U2>, std::add_volatile_t<T3>, T3>;
using T5 = std::conditional_t<std::is_lvalue_reference_v<U>, std::add_lvalue_reference_t<T4>, std::conditional_t<std::is_rvalue_reference_v<U>, std::add_rvalue_reference_t<T4>, T4>>;
public:
using type = T5;
};
template <typename T, typename U>
using copy_cvref_t = TYPENAME copy_cvref<T, U>::type;
template <typename T, typename U>
constexpr decltype(auto) forward_to(U&& d)
{
return static_cast<copy_cvref_t<T, decltype(d)>>(d);
}
template <typename ...>
struct get_type;
template <typename T, typename ...Us>
struct get_type<T, Us...>
{
using first_type = T;
};
template <typename T, typename U, typename ...Vs>
struct get_type<T, U, Vs...>
{
using first_type = T;
using second_type = U;
};
template <typename ...Ts>
using get_first_type_t = TYPENAME get_type<Ts...>::first_type;
template <typename ...Ts>
using get_second_type_t = TYPENAME get_type<Ts...>::second_type;
template <typename ...>
struct type_pack {};
template <typename T>
void is_implicitly_default_constructible_helper(const T&);
template <typename T>
concept ImplicitlyDefaultConstructible = requires { is_implicitly_default_constructible_helper<T>({}); };
template <typename T>
concept BooleanTestable = std::convertible_to<T, bool> &&
requires (T&& t)
{
{ !std::forward<T>(t) } -> std::convertible_to<bool>;
};
template <typename T, typename U>
concept ComparableWith =
requires (const T& t, const U& u)
{
{ t < u } -> BooleanTestable;
{ u < t } -> BooleanTestable;
};
template <typename T>
concept Comparable = ComparableWith<T, T>;
template <std::size_t I, typename T>
class tuple_unit
{
public:
static constexpr std::size_t index = I;
using type = T;
template <typename ...Us>
constexpr tuple_unit(tuple_unit_constructor_tag<0>, Us&& ...us) : m_data(std::forward<Us>(us)...) {}
template <typename A, typename ...Us>
constexpr tuple_unit(tuple_unit_constructor_tag<1>, const A& a, Us&& ...us)
requires std::uses_allocator_v<T, A>&& std::is_constructible_v<T, std::allocator_arg_t, const A&, Us...>
: m_data(std::allocator_arg, a, std::forward<Us>(us)...) {}
template <typename A, typename ...Us>
constexpr tuple_unit(tuple_unit_constructor_tag<1>, const A& a, Us&& ...us)
requires std::uses_allocator_v<T, A>&& std::is_constructible_v<T, Us..., const A&>
: m_data(std::forward<Us>(us)..., a) {}
template <typename A, typename ...Us>
constexpr tuple_unit(tuple_unit_constructor_tag<1>, const A& a, Us&& ...us)
requires std::negation_v<std::uses_allocator<T, A>>
: m_data(std::forward<Us>(us)...) {}
tuple_unit(const tuple_unit& other) = default;
tuple_unit(tuple_unit&& other) = default;
template <typename U>
constexpr void assign(tuple_unit_assign_tag<0>, U&& u)
{
m_data = std::forward<U>(u).get();
}
template <typename U>
constexpr void assign(tuple_unit_assign_tag<1>, U&& u) const
{
m_data = std::forward<U>(u).get();
}
template <typename U>
constexpr bool equal(const tuple_unit<I, U>& other) const&
{
return m_data == other.m_data;
}
template <typename U>
constexpr auto three_way_compare(const tuple_unit<I, U>& other) const&
{
if constexpr (std::three_way_comparable_with<T, U>)
{
return get() <=> other.get();
}
else
{
return get() < other.get() ? std::weak_ordering::less : other.get() < get() ? std::weak_ordering::greater : std::weak_ordering::equivalent;
}
}
constexpr T& get() &
{
return m_data;
}
constexpr const T& get() const&
{
return m_data;
}
constexpr T get() &&
{
return std::move(m_data);
}
constexpr const T get() const&&
{
return std::move(m_data);
}
constexpr void swap(tuple_unit& other)
{
using std::swap;
swap(m_data, other.m_data);
}
constexpr void swap(const tuple_unit& other) const
{
using std::swap;
swap(m_data, other.m_data);
}
private:
T m_data;
};
template <typename, typename ...>
class tuple_base;
template <std::size_t ...Is, typename ...Ts>
class EMPTY_BASES tuple_base<std::index_sequence<Is...>, Ts...> : public tuple_unit<Is, Ts>...
{
private:
template <std::size_t ...>
struct get_first_integer;
template <std::size_t I2, std::size_t ...Js2>
struct get_first_integer<I2, Js2...>
{
static constexpr std::size_t value = I2;
};
// GCC class template explicit specialization bug workaround
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282 for details
#if defined(__GNUC__) && !(defined(__clang__) || defined(__INTEL_COMPILER))
template <typename, std::size_t ...Is2>
struct are_numbers_begin_with_zero : std::bool_constant<(get_first_integer<Is2...>::value == 0)> {};
template <typename T2>
struct are_numbers_begin_with_zero<T2> : std::false_type {};
template <std::size_t ...Is2>
static constexpr bool are_numbers_begin_with_zero_v = are_numbers_begin_with_zero<void, Is2...>::value;
template <typename, std::size_t ...>
struct are_numbers_continuous_helper;
template <typename T2, std::size_t I2, std::size_t J2, std::size_t ...Ks2>
struct are_numbers_continuous_helper<T2, I2, J2, Ks2...>
{
static constexpr bool value = std::conditional_t<J2 - I2 == 1, are_numbers_continuous_helper<T2, J2, Ks2...>, std::false_type>::value;
};
template <typename T2, std::size_t I2>
struct are_numbers_continuous_helper<T2, I2>
{
static constexpr bool value = true;
};
template <typename T2>
struct are_numbers_continuous_helper<T2>
{
static constexpr bool value = false;
};
template <std::size_t ...Is2>
struct are_numbers_continuous : std::bool_constant<are_numbers_continuous_helper<void, Is2...>::value> {};
template <std::size_t ...Is2>
static constexpr bool are_numbers_continuous_v = are_numbers_continuous<Is2...>::value;
#else
template <std::size_t ...Is2>
struct are_numbers_begin_with_zero : std::bool_constant<(get_first_integer<Is2...>::value == 0)> {};
template <>
struct are_numbers_begin_with_zero<> : std::false_type {};
template <std::size_t ...Is2>
static constexpr bool are_numbers_begin_with_zero_v = are_numbers_begin_with_zero<Is2...>::value;
template <std::size_t ...>
struct are_numbers_continuous_helper;
template <std::size_t I2, std::size_t J2, std::size_t ...Ks2>
struct are_numbers_continuous_helper<I2, J2, Ks2...>
{
static constexpr bool value = std::conditional_t<J2 - I2 == 1, are_numbers_continuous_helper<J2, Ks2...>, std::false_type>::value;
};
template <std::size_t I2>
struct are_numbers_continuous_helper<I2>
{
static constexpr bool value = true;
};
template <>
struct are_numbers_continuous_helper<>
{
static constexpr bool value = false;
};
template <std::size_t ...Is2>
struct are_numbers_continuous : std::bool_constant<are_numbers_continuous_helper<Is2...>::value> {};
template <std::size_t ...Is2>
static constexpr bool are_numbers_continuous_v = are_numbers_continuous<Is2...>::value;
#endif
static_assert(are_numbers_begin_with_zero_v<Is...>, "Indices in std::index_sequence must begin with zero.");
static_assert(are_numbers_continuous_v<Is...>, "indices in std::index_sequence must be continuous.");
static_assert(sizeof...(Is) == sizeof...(Ts), "The number of indices in std::index_sequence must be equal to the number of types in parameter pack ...Ts.");
public:
using indices = std::index_sequence<Is...>;
using types = type_pack<Ts...>;
using tuple_unit<Is, Ts>::get...;
constexpr tuple_base(tuple_base_constructor_tag<0>)
: tuple_unit<Is, Ts>(tuple_unit_constructor_tag<0>{})... {}
template <typename ...Us>
constexpr tuple_base(tuple_base_constructor_tag<1>, Us&& ...us)
: tuple_unit<Is, Ts>(tuple_unit_constructor_tag<0>{}, std::forward<Us>(us))... {}
template <typename U, typename ...Vs>
constexpr tuple_base(tuple_base_constructor_tag<2>, U&& other, type_pack<Vs...>)
: tuple_unit<Is, Ts>(tuple_unit_constructor_tag<0>{}, forward_to<tuple_unit<Is, Vs>>(other).get())... {}
tuple_base(const tuple_base& other) = default;
tuple_base(tuple_base&& other) = default;
template <typename A>
constexpr tuple_base(tuple_base_constructor_tag<5>, const A& a)
: tuple_unit<Is, Ts>(tuple_unit_constructor_tag<1>{}, a)... {}
template <typename A, typename ...Us>
constexpr tuple_base(tuple_base_constructor_tag<6>, const A& a, Us&& ...us)
: tuple_unit<Is, Ts>(tuple_unit_constructor_tag<1>{}, a, std::forward<Us>(us))... {}
template <typename A, typename U, typename ...Vs>
constexpr tuple_base(tuple_base_constructor_tag<7>, const A& a, U&& other, type_pack<Vs...>)
: tuple_unit<Is, Ts>(tuple_unit_constructor_tag<1>{}, a, forward_to<tuple_unit<Is, Vs>>(other).get())... {}
template <std::size_t ...Is2, typename ...Ts2, std::size_t ...Js2, typename ...Us2>
constexpr tuple_base(tuple_base_constructor_tag<8>, tuple_base<std::index_sequence<Is2...>, Ts2...>& t1, tuple_base<std::index_sequence<Js2...>, Us2...>& t2)
: tuple_unit<0, get_first_type_t<Ts...>>(tuple_unit_constructor_tag<0>{}, static_cast<tuple_unit<Is2, Ts2>&>(t1).get()...), tuple_unit<1, get_second_type_t<Ts...>>(tuple_unit_constructor_tag<0>{}, static_cast<tuple_unit<Js2, Us2>&>(t2).get()...) {}
template <typename ...Us>
constexpr void assign(tuple_base_assign_tag<0>, const tuple_base<std::index_sequence<Is...>, Us...>& other)
{
(..., static_cast<tuple_unit<Is, Ts>&>(*this).assign(tuple_unit_assign_tag<0>{}, static_cast<const tuple_unit<Is, Us>&>(other)));
}
template <typename ...Us>
constexpr void assign(tuple_base_assign_tag<1>, const tuple_base<std::index_sequence<Is...>, Us...>& other) const
{
(..., static_cast<const tuple_unit<Is, Ts>&>(*this).assign(tuple_unit_assign_tag<1>{}, static_cast<const tuple_unit<Is, Us>&>(other)));
}
template <typename ...Us>
constexpr void assign(tuple_base_assign_tag<2>, tuple_base<std::index_sequence<Is...>, Us...>&& other)
{
(..., static_cast<tuple_unit<Is, Ts>&>(*this).assign(tuple_unit_assign_tag<0>{}, static_cast<tuple_unit<Is, Us>&&>(other)));
}
template <typename ...Us>
constexpr void assign(tuple_base_assign_tag<3>, tuple_base<std::index_sequence<Is...>, Us...>&& other) const
{
(..., static_cast<const tuple_unit<Is, Ts>&>(*this).assign(tuple_unit_assign_tag<1>{}, static_cast<tuple_unit<Is, Us>&&>(other)));
}
template <typename ...Us>
constexpr bool equal(const tuple_base<std::index_sequence<Is...>, Us...>& other) const&
{
return (... && (static_cast<const tuple_unit<Is, Ts>&>(*this).equal(static_cast<const tuple_unit<Is, Us>&>(other))));
}
template <typename T2, typename U2>
struct simple_pair
{
simple_pair(const T2& t, const U2& u) : rt(t), ru(u) {}
const T2& rt;
const U2& ru;
};
template <typename T2, typename U2>
static constexpr auto three_way_compare_tuple_units(const simple_pair<T2, U2>& p) -> std::compare_three_way_result_t<typename T2::type, typename U2::type>
{
if (auto r = p.rt.three_way_compare(p.ru); r != 0)
{
return r;
}
else
{
return std::strong_ordering::equal;
}
}
template <typename T2, typename U2, typename ...Ts2, typename ...Us2>
static constexpr auto three_way_compare_tuple_units(const simple_pair<T2, U2>& p, const simple_pair<Ts2, Us2>& ...ps) -> std::common_comparison_category_t<std::compare_three_way_result_t<typename T2::type, typename U2::type>, std::compare_three_way_result_t<typename Ts2::type, typename Us2::type>...>
{
if (auto r = p.rt.three_way_compare(p.ru); r != 0)
{
return r;
}
else
{
return three_way_compare_tuple_units(ps...);
}
}
template <typename ...Us>
constexpr auto three_way_compare(const tuple_base<std::index_sequence<Is...>, Us...>& other) const&
{
return three_way_compare_tuple_units(simple_pair<tuple_unit<Is, Ts>, tuple_unit<Is, Us>>(static_cast<const tuple_unit<Is, Ts>&>(*this), static_cast<const tuple_unit<Is, Us>&>(other))...);
}
constexpr void swap(tuple_base& other)
{
(..., static_cast<tuple_unit<Is, Ts>&>(*this).swap(static_cast<tuple_unit<Is, Ts>&>(other)));
}
constexpr void swap(const tuple_base& other) const
{
(..., static_cast<const tuple_unit<Is, Ts>&>(*this).swap(static_cast<const tuple_unit<Is, Ts>&>(other)));
}
};
template <typename T>
struct is_tuple_base : std::false_type {};
template <std::size_t ...Is, typename ...Ts>
struct is_tuple_base<tuple_base<std::index_sequence<Is...>, Ts...>> : std::true_type {};
template <typename T>
inline constexpr bool is_tuple_base_v = is_tuple_base<T>::value;
template <typename>
struct is_array : std::false_type {};
template <typename T, std::size_t N>
struct is_array<std::array<T, N>> : std::true_type {};
template <typename T>
inline constexpr bool is_array_v = is_array<T>::value;
template <typename, typename>
struct make_n_types_helper;
template <typename T, std::size_t ...Is>
struct make_n_types_helper<T, std::index_sequence<Is...>>
{
using type = type_pack<typename std::conditional_t<true, T, std::integral_constant<std::size_t, Is>>...>;
};
template <typename T, std::size_t N>
struct make_n_types
{
using type = TYPENAME make_n_types_helper<T, std::make_index_sequence<N>>::type;
};
template <typename T, std::size_t N>
using make_n_types_t = TYPENAME make_n_types<T, N>::type;
template <typename T, typename U, std::size_t ...Is, typename ...Ts, std::size_t ...Js, typename ...Us>
constexpr auto tuple_cat_impl_helper_tuple_base_tuple_base(T&& t, U&& u, std::index_sequence<Is...>, type_pack<Ts...>, std::index_sequence<Js...>, type_pack<Us...>)
{
return tuple_base<std::index_sequence<Is..., (Js + sizeof...(Is))...>, Ts..., Us...>(tuple_base_constructor_tag<1>{}, forward_to<tuple_unit<Is, Ts>>(t).get()..., forward_to<tuple_unit<Js, Us>>(u).get()...);
}
template <typename T, typename U, std::size_t ...Is, typename ...Ts, std::size_t ...Js, typename ...Us>
constexpr auto tuple_cat_impl_helper_tuple_base_array(T&& t, U&& u, std::index_sequence<Is...>, type_pack<Ts...>, std::index_sequence<Js...>, type_pack<Us...>)
{
return tuple_base<std::index_sequence<Is..., (Js + sizeof...(Is))...>, Ts..., Us...>(tuple_base_constructor_tag<1>{}, forward_to<tuple_unit<Is, Ts>>(t).get()..., std::get<Js>(std::forward<U>(u))...);
}
template <typename T, typename U, std::size_t ...Is, typename ...Ts, std::size_t ...Js, typename ...Us>
constexpr auto tuple_cat_impl_helper_array_tuple_base(T&& t, U&& u, std::index_sequence<Is...>, type_pack<Ts...>, std::index_sequence<Js...>, type_pack<Us...>)
{
return tuple_base<std::index_sequence<Is..., (Js + sizeof...(Is))...>, Ts..., Us...>(tuple_base_constructor_tag<1>{}, std::get<Is>(std::forward<T>(t))..., forward_to<tuple_unit<Js, Us>>(u).get()...);
}
template <typename T, typename U, std::size_t ...Is, typename ...Ts, std::size_t ...Js, typename ...Us>
constexpr auto tuple_cat_impl_helper_array_array(T&& t, U&& u, std::index_sequence<Is...>, type_pack<Ts...>, std::index_sequence<Js...>, type_pack<Us...>)
{
return tuple_base<std::index_sequence<Is..., (Js + sizeof...(Is))...>, Ts..., Us...>(tuple_base_constructor_tag<1>{}, std::get<Is>(std::forward<T>(t))..., std::get<Js>(std::forward<U>(u))...);
}
template <typename T>
constexpr auto tuple_cat_impl_helper(T&& t)
{
return t;
}
template <typename T, typename U, typename ...Vs>
requires is_tuple_base_v<std::remove_cvref_t<T>> && is_tuple_base_v<std::remove_cvref_t<U>>
constexpr auto tuple_cat_impl_helper(T&& t, U&& u, Vs&& ...vs)
{
return tuple_cat_impl_helper(tuple_cat_impl_helper_tuple_base_tuple_base(std::forward<T>(t), std::forward<U>(u), typename std::remove_cvref_t<T>::indices{}, typename std::remove_cvref_t<T>::types{}, typename std::remove_cvref_t<U>::indices{}, typename std::remove_cvref_t<U>::types{}), std::forward<Vs>(vs)...);
}
template <typename T, typename U, typename ...Vs>
requires is_tuple_base_v<std::remove_cvref_t<T>> && is_array_v<std::remove_cvref_t<U>>
constexpr auto tuple_cat_impl_helper(T&& t, U&& u, Vs&& ...vs)
{
return tuple_cat_impl_helper(tuple_cat_impl_helper_tuple_base_array(std::forward<T>(t), std::forward<U>(u), typename std::remove_cvref_t<T>::indices{}, typename std::remove_cvref_t<T>::types{}, std::make_index_sequence<std::tuple_size_v<std::remove_cvref_t<U>>>{}, make_n_types_t<typename std::remove_cvref_t<U>::value_type, std::tuple_size_v<std::remove_cvref_t<U>>>{}), std::forward<Vs>(vs)...);
}
template <typename T, typename U, typename ...Vs>
requires is_array_v<std::remove_cvref_t<T>> && is_tuple_base_v<std::remove_cvref_t<U>>
constexpr auto tuple_cat_impl_helper(T&& t, U&& u, Vs&& ...vs)
{
return tuple_cat_impl_helper(tuple_cat_impl_helper_array_tuple_base(std::forward<T>(t), std::forward<U>(u), std::make_index_sequence<std::tuple_size_v<std::remove_cvref_t<T>>>{}, make_n_types_t<typename std::remove_cvref_t<T>::value_type, std::tuple_size_v<std::remove_cvref_t<T>>>{}, typename std::remove_cvref_t<U>::indices{}, typename std::remove_cvref_t<U>::types{}), std::forward<Vs>(vs)...);
}
template <typename T, typename U, typename ...Vs>
requires is_array_v<std::remove_cvref_t<T>> && is_array_v<std::remove_cvref_t<U>>
constexpr auto tuple_cat_impl_helper(T&& t, U&& u, Vs&& ...vs)
{
return tuple_cat_impl_helper(tuple_cat_impl_helper_array_array(std::forward<T>(t), std::forward<U>(u), std::make_index_sequence<std::tuple_size_v<std::remove_cvref_t<T>>>{}, make_n_types_t<typename std::remove_cvref_t<T>::value_type, std::tuple_size_v<std::remove_cvref_t<T>>>{}, std::make_index_sequence<std::tuple_size_v<std::remove_cvref_t<U>>>{}, make_n_types_t<typename std::remove_cvref_t<U>::value_type, std::tuple_size_v<std::remove_cvref_t<U>>>{}), std::forward<Vs>(vs)...);
}
template <typename ...Ts>
constexpr auto tuple_cat_impl(Ts&& ...ts)
{
return tuple_cat_impl_helper(std::forward<Ts>(ts)...);
}
template <typename F, typename T, std::size_t ...Is, typename ...Ts>
constexpr decltype(auto) apply_impl_tuple_base(F&& f, T&& t, std::index_sequence<Is...>, type_pack<Ts...>)
{
return std::invoke(std::forward<F>(f), forward_to<tuple_unit<Is, Ts>>(t).get()...);
}
template <typename F, typename T, std::size_t ...Is>
constexpr decltype(auto) apply_impl_array(F&& f, T&& t, std::index_sequence<Is...>)
{
return std::invoke(std::forward<F>(f), std::get<Is>(t)...);
}
template <typename F, typename T>
requires is_tuple_base_v<std::remove_cvref_t<T>>
constexpr decltype(auto) apply_impl(F&& f, T&& t)
{
return apply_impl_tuple_base(std::forward<F>(f), std::forward<T>(t), typename std::remove_cvref_t<T>::indices{}, typename std::remove_cvref_t<T>::types{});
}
template <typename F, typename T>
requires is_array_v<std::remove_cvref_t<T>>
constexpr decltype(auto) apply_impl(F&& f, T&& t)
{
return apply_impl_array(std::forward<F>(f), std::forward<T>(t), std::make_index_sequence<std::tuple_size_v<T>>{});
}
}
#endif // TUPLE_BASE_HPP_INCLUDED
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment