228 lines
6.5 KiB
C++
228 lines
6.5 KiB
C++
#pragma once
|
|
|
|
#include "RE/M/MemoryManager.h"
|
|
|
|
namespace RE
|
|
{
|
|
template <class T1, class T2>
|
|
class BSTTuple
|
|
{
|
|
public:
|
|
using first_type = T1;
|
|
using second_type = T2;
|
|
|
|
// 1)
|
|
BSTTuple() //
|
|
noexcept(std::is_nothrow_default_constructible_v<first_type> &&
|
|
std::is_nothrow_default_constructible_v<second_type>) //
|
|
requires(std::is_default_constructible_v<first_type> &&
|
|
std::is_default_constructible_v<second_type>)
|
|
:
|
|
first(),
|
|
second()
|
|
{}
|
|
|
|
// 2)
|
|
explicit(!std::is_convertible_v<const first_type&, first_type> ||
|
|
!std::is_convertible_v<const second_type&, second_type>) //
|
|
BSTTuple(const first_type& a_first, const second_type& a_second) //
|
|
noexcept(std::is_nothrow_copy_constructible_v<first_type> &&
|
|
std::is_nothrow_copy_constructible_v<second_type>) //
|
|
requires(std::is_copy_constructible_v<first_type> &&
|
|
std::is_copy_constructible_v<second_type>)
|
|
:
|
|
first(a_first),
|
|
second(a_second)
|
|
{}
|
|
|
|
// 3)
|
|
template <class U1, class U2>
|
|
explicit(!std::is_convertible_v<U1&&, first_type> ||
|
|
!std::is_convertible_v<U2&&, second_type>) //
|
|
BSTTuple(U1&& a_first, U2&& a_second) //
|
|
noexcept(std::is_nothrow_constructible_v<first_type, U1&&> &&
|
|
std::is_nothrow_constructible_v<second_type, U2&&>) //
|
|
requires(std::is_constructible_v<first_type, U1 &&> &&
|
|
std::is_constructible_v<second_type, U2 &&>)
|
|
:
|
|
first(std::forward<U1>(a_first)),
|
|
second(std::forward<U2>(a_second))
|
|
{}
|
|
|
|
// 4)
|
|
template <class U1, class U2>
|
|
explicit(!std::is_convertible_v<const U1&, first_type> ||
|
|
!std::is_convertible_v<const U2&, second_type>) //
|
|
BSTTuple(const BSTTuple<U1, U2>& a_rhs) //
|
|
noexcept(std::is_nothrow_constructible_v<first_type, const U1&> &&
|
|
std::is_nothrow_constructible_v<second_type, const U2&>) //
|
|
requires(std::is_constructible_v<first_type, const U1&> &&
|
|
std::is_constructible_v<second_type, const U2&>)
|
|
:
|
|
first(a_rhs.first),
|
|
second(a_rhs.second)
|
|
{}
|
|
|
|
// 5)
|
|
template <class U1, class U2>
|
|
explicit(!std::is_convertible_v<U1&&, first_type> ||
|
|
!std::is_convertible_v<U2&&, second_type>) //
|
|
BSTTuple(BSTTuple<U1, U2>&& a_rhs) //
|
|
noexcept(std::is_nothrow_constructible_v<first_type, U1&&> &&
|
|
std::is_nothrow_constructible_v<second_type, U2&&>) //
|
|
requires(std::is_constructible_v<first_type, U1 &&> &&
|
|
std::is_constructible_v<second_type, U2 &&>)
|
|
:
|
|
first(std::forward<U1>(a_rhs.first)),
|
|
second(std::forward<U2>(a_rhs.second))
|
|
{}
|
|
|
|
// 6)
|
|
template <
|
|
class... Args1,
|
|
class... Args2>
|
|
BSTTuple(std::piecewise_construct_t, std::tuple<Args1...> a_firstArgs, std::tuple<Args2...> a_secondArgs) :
|
|
BSTTuple(a_firstArgs, a_secondArgs, std::index_sequence_for<Args1...>(), std::index_sequence_for<Args2...>())
|
|
{}
|
|
|
|
private:
|
|
// 6) impl
|
|
template <
|
|
class Tuple1,
|
|
class Tuple2,
|
|
std::size_t... I1,
|
|
std::size_t... I2>
|
|
BSTTuple(Tuple1& a_firstArgs, Tuple2& a_secondArgs, std::index_sequence<I1...>, std::index_sequence<I2...>) :
|
|
first(std::get<I1>(std::move(a_firstArgs))...),
|
|
second(std::get<I2>(std::move(a_secondArgs))...)
|
|
{}
|
|
|
|
public:
|
|
// 7)
|
|
BSTTuple(const BSTTuple&) = default;
|
|
|
|
// 8)
|
|
BSTTuple(BSTTuple&&) = default;
|
|
|
|
~BSTTuple() = default;
|
|
|
|
// 1)
|
|
BSTTuple& operator=(const BSTTuple& a_rhs) //
|
|
noexcept(std::is_nothrow_copy_assignable_v<first_type> &&
|
|
std::is_nothrow_copy_assignable_v<second_type>) //
|
|
requires(std::is_copy_assignable_v<first_type> &&
|
|
std::is_copy_assignable_v<second_type>)
|
|
{
|
|
if (this != std::addressof(a_rhs)) {
|
|
first = a_rhs.first;
|
|
second = a_rhs.second;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// 2)
|
|
template <class U1, class U2>
|
|
BSTTuple& operator=(const BSTTuple<U1, U2>& a_rhs) //
|
|
noexcept(std::is_nothrow_assignable_v<first_type&, const U1&> &&
|
|
std::is_nothrow_assignable_v<second_type&, const U2&>) //
|
|
requires(std::is_assignable_v<first_type&, const U1&> &&
|
|
std::is_assignable_v<second_type&, const U2&>)
|
|
{
|
|
first = a_rhs.first;
|
|
second = a_rhs.second;
|
|
return *this;
|
|
}
|
|
|
|
// 3)
|
|
BSTTuple& operator=(BSTTuple&& a_rhs) //
|
|
noexcept(std::is_nothrow_move_assignable_v<first_type> &&
|
|
std::is_nothrow_move_assignable_v<second_type>) //
|
|
requires(std::is_move_assignable_v<first_type> &&
|
|
std::is_move_assignable_v<second_type>)
|
|
{
|
|
if (this != std::addressof(a_rhs)) {
|
|
first = std::move(a_rhs.first);
|
|
second = std::move(a_rhs.second);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// 4)
|
|
template <class U1, class U2>
|
|
BSTTuple& operator=(BSTTuple<U1, U2>&& a_rhs) //
|
|
noexcept(std::is_nothrow_assignable_v<first_type&, U1> &&
|
|
std::is_nothrow_assignable_v<second_type&, U2>) //
|
|
requires(std::is_assignable_v<first_type&, U1> &&
|
|
std::is_assignable_v<second_type&, U2>)
|
|
{
|
|
first = std::move(a_rhs.first);
|
|
second = std::move(a_rhs.second);
|
|
return *this;
|
|
}
|
|
|
|
F4_HEAP_REDEFINE_NEW(BSTTuple<T1, T2>);
|
|
|
|
void swap(BSTTuple& a_rhs) //
|
|
noexcept(std::is_nothrow_swappable_v<first_type> &&
|
|
std::is_nothrow_swappable_v<second_type>)
|
|
{
|
|
using std::swap;
|
|
if (this != std::addressof(a_rhs)) {
|
|
swap(first, a_rhs.first);
|
|
swap(second, a_rhs.second);
|
|
}
|
|
}
|
|
|
|
// members
|
|
first_type first; // 00
|
|
second_type second; // ??
|
|
};
|
|
|
|
template <class T1, class T2>
|
|
[[nodiscard]] auto make_pair(T1&& a_first, T2&& a_second)
|
|
{
|
|
using result_t =
|
|
BSTTuple<
|
|
std::decay_t<T1>,
|
|
std::decay_t<T2>>;
|
|
return result_t(std::forward<T1>(a_first), std::forward<T2>(a_second));
|
|
}
|
|
|
|
template <class T1, class T2>
|
|
[[nodiscard]] auto make_tuple(T1&& a_first, T2&& a_second)
|
|
{
|
|
using result_t =
|
|
BSTTuple<
|
|
std::decay_t<T1>,
|
|
std::decay_t<T2>>;
|
|
return result_t(std::forward<T1>(a_first), std::forward<T2>(a_second));
|
|
}
|
|
|
|
template <class T1, class T2>
|
|
[[nodiscard]] bool operator==(const BSTTuple<T1, T2>& a_lhs, const BSTTuple<T1, T2>& a_rhs)
|
|
{
|
|
return a_lhs.first == a_rhs.first && a_lhs.second == a_rhs.second;
|
|
}
|
|
|
|
template <class T1, class T2>
|
|
[[nodiscard]] bool operator<(const BSTTuple<T1, T2>& a_lhs, const BSTTuple<T1, T2>& a_rhs)
|
|
{
|
|
return a_lhs.first < a_rhs.first ? true :
|
|
a_rhs.first < a_lhs.first ? false :
|
|
a_lhs.second < a_rhs.second ? true :
|
|
false;
|
|
}
|
|
|
|
template <class T1, class T2>
|
|
void swap(BSTTuple<T1, T2>& a_lhs, BSTTuple<T1, T2>& a_rhs) //
|
|
noexcept(noexcept(a_lhs.swap(a_rhs))) //
|
|
requires(std::is_swappable_v<T1> &&
|
|
std::is_swappable_v<T2>)
|
|
{
|
|
a_lhs.swap(a_rhs);
|
|
}
|
|
|
|
template <class T1, class T2>
|
|
BSTTuple(T1, T2) -> BSTTuple<T1, T2>;
|
|
}
|