#pragma once #include "yacppdic/detail/internal/misc_concepts.hpp" #include "yacppdic/detail/internal/tuple_indices.hpp" #include <concepts> #include <tuple> #include <type_traits> #include <utility> namespace yacppdic::internal { // Tag used to default initialize one or both of the pair's elements. class DefaultInitTag { }; class ValueInitTag { }; template <typename Value> concept ValueCanBeEmptyBase = std::is_empty_v<Value> && !std::is_final_v<Value>; template <typename Value, int Idx> class CompressedPairElement { public: using ValueReference = Value &; using ConstValueReference = const Value &; constexpr explicit CompressedPairElement(DefaultInitTag /*default_init_tag*/ ) noexcept {}; constexpr explicit CompressedPairElement(ValueInitTag /*value_init_tag*/) noexcept : _value() { } template <typename ElementValue> requires std::same_as<CompressedPairElement, std::decay_t<ElementValue>> // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) constexpr explicit CompressedPairElement(ElementValue &&value) noexcept : _value(std::forward<ElementValue>(value)) { } template <typename... Args, std::size_t... Indices> constexpr CompressedPairElement( std::piecewise_construct_t /*tag_type*/, std::tuple<Args...> args, TupleIndices<Indices...> /*tuple_indices*/ ) noexcept : _value(std::forward<Args>(std::get<Indices>(args))...) { } auto get() noexcept -> ValueReference { return _value; } [[nodiscard]] auto get() const noexcept -> ConstValueReference { return _value; } private: Value _value; }; template <typename Value, int Idx> requires ValueCanBeEmptyBase<Value> class CompressedPairElement<Value, Idx> : private Value { public: using ValueReference = Value &; using ConstValueReference = const Value &; constexpr CompressedPairElement() noexcept = default; constexpr explicit CompressedPairElement(DefaultInitTag /*unused*/) noexcept {} constexpr explicit CompressedPairElement(ValueInitTag /*unused*/) noexcept : Value() { } template <typename ElementValue> requires std::same_as<CompressedPairElement, typename std::decay<ElementValue>::type> // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) constexpr explicit CompressedPairElement(ElementValue &&value) noexcept : Value(std::forward<ElementValue>(value)) { } template <typename... Args, std::size_t... Indices> constexpr CompressedPairElement( std::piecewise_construct_t /*unused*/, std::tuple<Args...> args, TupleIndices<Indices...> /*unused*/ ) noexcept : Value(std::forward<Args>(std::get<Indices>(args))...) { } auto get() noexcept -> ValueReference { return *this; } [[nodiscard]] auto get() const noexcept -> ConstValueReference { return *this; } }; template <typename ValueOne, typename ValueTwo> requires NotSameAs<ValueOne, ValueTwo> class CompressedPair : private CompressedPairElement<ValueOne, 0>, private CompressedPairElement<ValueTwo, 1> { public: using BaseOne = CompressedPairElement<ValueOne, 0>; using BaseTwo = CompressedPairElement<ValueTwo, 1>; template <typename> requires std::default_initializable<ValueOne> && std::default_initializable<ValueTwo> constexpr CompressedPair() noexcept; template <typename FirstValue, typename SecondValue> constexpr CompressedPair( FirstValue &&first_value, SecondValue &&second_value ) noexcept; template <typename... ArgsOne, typename... ArgsTwo> constexpr CompressedPair( std::piecewise_construct_t piecewise_construct, std::tuple<ArgsOne...> first_args, std::tuple<ArgsTwo...> second_args ) noexcept; auto first() noexcept -> typename BaseOne::ValueReference; [[nodiscard]] auto first() const noexcept -> typename BaseOne::ConstValueReference; auto second() noexcept -> typename BaseTwo::ValueReference; [[nodiscard]] auto second() const noexcept -> typename BaseTwo::ConstValueReference; constexpr static auto get_first_base(CompressedPair *pair) noexcept -> BaseOne *; constexpr static auto get_second_base(CompressedPair *pair) noexcept -> BaseTwo *; }; } // namespace yacppdic::internal #include "compressed_pair-impl.hpp"