Skip to content

Commit 4c12ee5

Browse files
committed
Added "immutable" modifier
1 parent d6be4b9 commit 4c12ee5

File tree

4 files changed

+57
-4
lines changed

4 files changed

+57
-4
lines changed

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* Added modifier strong::immutable, which disables assignment
2+
and makes value_of() always return const references. Thanks
3+
David Hunder for the discussion that lead to this.
4+
15
v15 2024-07-07
26

37
* CMake package is now ARCH_INDEPENDENT, as it should be when it's

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ the underlying type can be, for compilers and standards that support it.
185185
186186
Available in `strong_type/hashable.hpp`.
187187
188+
* <A name="immutable"/>`strong::immutable`
189+
Disables assignment and makes `value_of` return a `const T&` or `const T&&`.
190+
Move construction is still supported.
188191
189192
* <A name="implicitly_convertible_to"/>`strong::implicitly_convertible_to<Ts...>`
190193
provides an `operator Ts() const` for each type `Ts`, providing the underlying

include/strong_type/type.hpp

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ struct default_constructible {
4646
};
4747
};
4848

49+
struct immutable {
50+
template <typename>
51+
class modifier {
52+
public:
53+
modifier() noexcept = default;
54+
modifier(const modifier&) = default;
55+
modifier(modifier&&) = default;
56+
~modifier() = default;
57+
modifier& operator=(const modifier&) = delete;
58+
modifier& operator=(modifier&&) = delete;
59+
};
60+
};
61+
4962
namespace impl {
5063
template<typename T>
5164
constexpr bool supports_default_construction(
@@ -58,6 +71,18 @@ template<typename T, typename ... V>
5871
using WhenConstructible = std::enable_if_t<std::is_constructible<T, V...>::value>;
5972
}
6073

74+
namespace impl {
75+
template <typename T>
76+
constexpr bool is_immutable(const ::strong::immutable::modifier<T>*)
77+
{
78+
return true;
79+
}
80+
constexpr bool is_immutable(const void*)
81+
{
82+
return false;
83+
}
84+
}
85+
6186
template<typename T, typename Tag, typename ... M>
6287
class type : public modifier<M, type<T, Tag, M...>> ... {
6388
public:
@@ -109,32 +134,36 @@ class type : public modifier<M, type<T, Tag, M...>> ... {
109134
swap(a._val, b._val);
110135
}
111136

137+
template<typename type_ = type>
112138
STRONG_NODISCARD
113-
constexpr T &value_of() & noexcept
139+
constexpr std::enable_if_t<!impl::is_immutable(static_cast<type_*>(nullptr)), T&>value_of() & noexcept
114140
{ return _val; }
115141

116142
STRONG_NODISCARD
117143
constexpr const T &value_of() const & noexcept
118144
{ return _val; }
119145

146+
template <typename type_ = type>
120147
STRONG_NODISCARD
121-
constexpr T &&value_of() && noexcept
148+
constexpr std::enable_if_t<!impl::is_immutable(static_cast<type_*>(nullptr)), T&&> value_of() && noexcept
122149
{ return std::move(_val); }
123150

124151
STRONG_NODISCARD
125152
constexpr const T &&value_of() const && noexcept
126153
{ return std::move(_val); }
127154

155+
template <typename type_ = type>
128156
STRONG_NODISCARD
129-
friend constexpr T &value_of(type &t) noexcept
157+
friend constexpr auto value_of(type &t) noexcept -> std::enable_if_t<!impl::is_immutable(static_cast<type_*>(nullptr)), T&>
130158
{ return t._val; }
131159

132160
STRONG_NODISCARD
133161
friend constexpr const T &value_of(const type &t) noexcept
134162
{ return t._val; }
135163

164+
template <typename type_ = type>
136165
STRONG_NODISCARD
137-
friend constexpr T &&value_of(type &&t) noexcept
166+
friend constexpr auto value_of(type &&t) noexcept -> std::enable_if_t<!impl::is_immutable(static_cast<type_*>(nullptr)), T&&>
138167
{ return std::move(t)._val; }
139168

140169
STRONG_NODISCARD

test/test.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,23 @@ static_assert(is_divisible<schdi, int>{},"");
643643
static_assert(std::is_same<int, decltype(scild{3}/scild{2})>{},"");
644644
static_assert(std::is_same<handle, decltype(schdi{3} / schdi{1})>{},"");
645645

646+
using immutable_int = strong::type<int, struct immutable_int_, strong::immutable>;
647+
648+
static_assert(! std::is_copy_assignable<immutable_int>{},"");
649+
static_assert(! std::is_move_assignable<immutable_int>{},"");
650+
static_assert(std::is_nothrow_copy_constructible<immutable_int>{},"");
651+
static_assert(std::is_nothrow_move_constructible<immutable_int>{},"");
652+
653+
static_assert(std::is_const<std::remove_reference_t<decltype(value_of(std::declval<immutable_int&&>()))>>{},"");
654+
static_assert(std::is_const<std::remove_reference_t<decltype(std::declval<immutable_int&&>().value_of())>>{},"");
655+
static_assert(std::is_const<std::remove_reference_t<decltype(value_of(std::declval<immutable_int&>()))>>{},"");
656+
static_assert(std::is_const<std::remove_reference_t<decltype(std::declval<immutable_int&>().value_of())>>{},"");
657+
static_assert(std::is_const<std::remove_reference_t<decltype(value_of(std::declval<const immutable_int&&>()))>>{},"");
658+
static_assert(std::is_const<std::remove_reference_t<decltype(std::declval<const immutable_int&&>().value_of())>>{},"");
659+
static_assert(std::is_const<std::remove_reference_t<decltype(value_of(std::declval<const immutable_int&>()))>>{},"");
660+
static_assert(std::is_const<std::remove_reference_t<decltype(std::declval<const immutable_int&>().value_of())>>{},"");
661+
662+
646663
#if STRONG_HAS_STD_FORMAT
647664
TEST_CASE("format")
648665
{

0 commit comments

Comments
 (0)