1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
#pragma once
// #################################################################################################
// A unique type generator
template <typename T, typename TAG, typename specialization = void>
class Unique
{
private:
T val; // no default initialization so that std::is_trivial_v<Unique<T>> == true
public:
using self = Unique<T, TAG, specialization>;
using type = T;
~Unique() = default;
constexpr explicit Unique(T b_)
: val{ b_ }
{
}
// rule of 5
constexpr Unique() = default;
constexpr Unique(Unique const&) = default;
constexpr Unique(Unique&&) = default;
constexpr Unique& operator=(Unique const&) = default;
constexpr Unique& operator=(Unique&&) = default;
// Forbid construction with any other class, especially with types that convert naturally to T.
// For instance, this prevents construction with a float when T is an integer type.
// Conversion will have to be explicit and the developer will be aware of it.
// However we want to keep the same-type copy constructors (including with an inherited class), hence the enable_if stuff.
template <typename AnyOtherClass, std::enable_if_t<!std::is_base_of_v<self, std::decay_t<AnyOtherClass>>, bool> = true>
Unique(AnyOtherClass const&) = delete;
template <typename AnyOtherClass, std::enable_if_t<!std::is_base_of_v<self, std::decay_t<AnyOtherClass>>, bool> = true>
Unique(AnyOtherClass&&) = delete;
// can't implicitly affect from base type
Unique& operator=(T const&) = delete;
constexpr Unique& operator=(T&&) = delete;
// cast
constexpr operator T const&() const noexcept { return val; }
constexpr T const& value() const noexcept { return val; }
// pre-increment
auto& operator++() noexcept
{
++val;
return *this;
}
// post-increment
auto operator++(int) noexcept
{
return Unique<T, TAG>{ std::exchange(val, val + 1) };
}
// pre-decrement
auto& operator--() noexcept
{
--val;
return *this;
}
// post-decrement
auto operator--(int) noexcept
{
return Unique<T, TAG>{ std::exchange(val, val - 1) };
}
};
template <typename T, typename TAG>
class Unique<T, TAG, std::enable_if_t<!std::is_scalar_v<T>>>
: public T
{
public:
using self = Unique<T, TAG, void>;
using type = T;
using T::T;
explicit Unique(T const& b_)
: T{ b_ }
{
}
// rule of 5
constexpr Unique() = default;
constexpr Unique(Unique const&) = default;
constexpr Unique(Unique&&) = default;
constexpr Unique& operator=(Unique const&) = default;
constexpr Unique& operator=(Unique&&) = default;
// Forbid construction with any other class, especially with types that convert naturally to T.
// For instance, this prevents construction with a float when T is an integer type.
// Conversion will have to be explicit and the developer will be aware of it.
// However we want to keep the same-type copy constructors (including with an inherited class), hence the enable_if stuff.
template <typename AnyOtherClass, std::enable_if_t<!std::is_base_of_v<self, std::decay_t<AnyOtherClass>>, bool> = true>
Unique(AnyOtherClass const&) = delete;
template <typename AnyOtherClass, std::enable_if_t<!std::is_base_of_v<self, std::decay_t<AnyOtherClass>>, bool> = true>
Unique(AnyOtherClass&&) = delete;
// can't implicitly affect from base type
Unique& operator=(T const&) = delete;
constexpr Unique& operator=(T&&) = delete;
};
#define DECLARE_UNIQUE_TYPE(_name, _type) using _name = Unique<_type, class Unique_##_name##_Tag>
|