|
#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 |