25template<std::unsigned_integral T, fixed_string Tag, std::size_t Max = std::numeric_limits<T>::max() - 1>
28 static_assert(Max < std::numeric_limits<T>::max(),
"Max must be at least one less than the maximum value of T");
32 constexpr static value_type max = Max;
33 constexpr static value_type invalid = max + 1;
34 constexpr static value_type mask =
static_cast<value_type
>((1ULL << std::bit_width(invalid)) - 1);
36 constexpr tagged_id() noexcept : value(invalid) {}
42 constexpr explicit tagged_id(std::integral
auto rhs) noexcept : value(narrow_cast<value_type>(rhs))
44 hi_axiom(holds_invariant() and value != invalid);
47 constexpr tagged_id(std::nullopt_t) noexcept : value(invalid) {}
49 constexpr tagged_id(nullptr_t) noexcept : value(invalid) {}
51 constexpr tagged_id& operator=(std::integral
auto rhs)
noexcept
53 value = narrow_cast<value_type>(rhs);
54 hi_axiom(holds_invariant() and value != invalid);
58 constexpr tagged_id& operator=(std::nullopt_t)
noexcept
64 constexpr tagged_id& operator=(nullptr_t)
noexcept
70 template<std::
integral O>
71 constexpr explicit operator O()
const noexcept
73 hi_axiom(value != invalid);
74 return narrow_cast<O>(value);
77 constexpr explicit operator bool()
const noexcept
79 return value != invalid;
82 [[nodiscard]]
constexpr value_type
const& operator*()
const noexcept
87 [[nodiscard]]
constexpr std::size_t hash()
const noexcept
92 [[nodiscard]]
constexpr auto operator<=>(
tagged_id const &)
const noexcept =
default;
94 [[nodiscard]]
constexpr bool operator==(
tagged_id const&)
const noexcept =
default;
96 [[nodiscard]]
constexpr bool operator==(nullptr_t)
const noexcept
98 return value == invalid;
101 [[nodiscard]]
constexpr bool operator==(std::nullopt_t)
const noexcept
103 return value == invalid;
107 [[nodiscard]]
constexpr bool operator==(
signed char rhs)
const noexcept {
return value == rhs; }
108 [[nodiscard]]
constexpr bool operator==(
signed short rhs)
const noexcept {
return value == rhs; }
109 [[nodiscard]]
constexpr bool operator==(
signed int rhs)
const noexcept {
return value == rhs; }
110 [[nodiscard]]
constexpr bool operator==(
signed long rhs)
const noexcept {
return value == rhs; }
111 [[nodiscard]]
constexpr bool operator==(
signed long long rhs)
const noexcept {
return value == rhs; }
112 [[nodiscard]]
constexpr bool operator==(
unsigned char rhs)
const noexcept {
return value == rhs; }
113 [[nodiscard]]
constexpr bool operator==(
unsigned short rhs)
const noexcept {
return value == rhs; }
114 [[nodiscard]]
constexpr bool operator==(
unsigned int rhs)
const noexcept {
return value == rhs; }
115 [[nodiscard]]
constexpr bool operator==(
unsigned long rhs)
const noexcept {
return value == rhs; }
116 [[nodiscard]]
constexpr bool operator==(
unsigned long long rhs)
const noexcept {
return value == rhs; }
119 [[nodiscard]]
bool holds_invariant()
const noexcept
121 return value <= max or value == invalid;
126 return std::format(
"{}:{}", Tag, rhs.value);
131 return lhs << to_string(rhs);