Skip to content

Commit df6e9b6

Browse files
committed
New issue from Hewill: "constant_wrapper's pseudo-mutators are underconstrained "
1 parent 0802eb5 commit df6e9b6

File tree

1 file changed

+139
-0
lines changed

1 file changed

+139
-0
lines changed

xml/issue4383.xml

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='no'?>
2+
<!DOCTYPE issue SYSTEM "lwg-issue.dtd">
3+
4+
<issue num="4383" status="New">
5+
<title>`constant_wrapper`'s pseudo-mutators are underconstrained
6+
</title>
7+
<section>
8+
<sref ref="[const.wrap.class]"/>
9+
</section>
10+
<submitter>Hewill Kang</submitter>
11+
<date>24 Sep 2025</date>
12+
<priority>99</priority>
13+
14+
<discussion>
15+
<p>
16+
Unlike other operators, `constant_wrapper`'s pseudo-mutators only require that the wrapped type has
17+
corresponding mutators, but do not require them to be <code>constexpr</code> or to return a sensible value.
18+
This inconsistency loses the SFINAE friendliness (<a href="https://godbolt.org/z/r4z1GGT6f">demo</a>):
19+
</p>
20+
<blockquote><pre>
21+
#include &lt;type_traits&gt;
22+
23+
void test(auto t) {
24+
if constexpr (requires { +t; }) // ok
25+
+t;
26+
if constexpr (requires { -t; }) // ok
27+
-t;
28+
if constexpr (requires { ++t; }) // <span style="color:#C80000;font-weight:bold">hard error</span>
29+
++t;
30+
if constexpr (requires { --t; }) // <span style="color:#C80000;font-weight:bold">hard error</span>
31+
--t;
32+
}
33+
34+
struct S {
35+
/* constexpr */ int operator+() const { return 0; }
36+
/* constexpr */ int operator++() { return 0; }
37+
constexpr void operator-() const { }
38+
constexpr void operator--() { }
39+
};
40+
41+
int main() {
42+
test(std::cw&lt;S{}&gt;);
43+
}
44+
</pre></blockquote>
45+
<p>
46+
Since these pseudo-mutators have constraints, it is reasonable to further require constant
47+
expressions.
48+
</p>
49+
</discussion>
50+
51+
<resolution>
52+
<p>
53+
This wording is relative to <paper num="N5014"/>.
54+
</p>
55+
56+
<ol>
57+
58+
<li><p>Modify <sref ref="[const.wrap.class]"/>, class template <tt>constant_wrapper</tt> synopsis, as indicated:</p>
59+
60+
<blockquote class="note">
61+
<p>
62+
[<i>Drafting note:</i> The requires clause follows the form of `constant_wrapper`'s
63+
function call operator.]
64+
</p>
65+
</blockquote>
66+
67+
<blockquote>
68+
<pre>
69+
struct <i>cw-operators</i> { // <i>exposition only</i>
70+
[&hellip;]
71+
// <i>pseudo-mutators</i>
72+
template&lt;<i>constexpr-param</i> T&gt;
73+
constexpr auto operator++(this T) noexcept
74+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>++x<ins>&gt;()</ins>; }
75+
{ return constant_wrapper&lt;[] { auto c = T::value; return ++c; }()&gt;{}; }
76+
template&lt;<i>constexpr-param</i> T&gt;
77+
constexpr auto operator++(this T, int) noexcept
78+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x++<ins>&gt;()</ins>; }
79+
{ return constant_wrapper&lt;[] { auto c = T::value; return c++; }()&gt;{}; }
80+
81+
template&lt;<i>constexpr-param</i> T&gt;
82+
constexpr auto operator--(this T) noexcept
83+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>--x<ins>&gt;()</ins>; }
84+
{ return constant_wrapper&lt;[] { auto c = T::value; return --c; }()&gt;{}; }
85+
template&lt;<i>constexpr-param</i> T&gt;
86+
constexpr auto operator--(this T, int) noexcept
87+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x--<ins>&gt;()</ins>; }
88+
{ return constant_wrapper&lt;[] { auto c = T::value; return c--; }()&gt;{}; }
89+
90+
template&lt;<i>constexpr-param</i> T, <i>constexpr-param</i> R&gt;
91+
constexpr auto operator+=(this T, R) noexcept
92+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x += R::value<ins>&gt;()</ins>; }
93+
{ return constant_wrapper&lt;[] { auto v = T::value; return v += R::value; }()&gt;{}; }
94+
template&lt;<i>constexpr-param</i> T, <i>constexpr-param</i> R&gt;
95+
constexpr auto operator-=(this T, R) noexcept
96+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x -= R::value<ins>&gt;()</ins>; }
97+
{ return constant_wrapper&lt;[] { auto v = T::value; return v -= R::value; }()&gt;{}; }
98+
template&lt;<i>constexpr-param</i> T, <i>constexpr-param</i> R&gt;
99+
constexpr auto operator*=(this T, R) noexcept
100+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x *= R::value<ins>&gt;()</ins>; }
101+
{ return constant_wrapper&lt;[] { auto v = T::value; return v *= R::value; }()&gt;{}; }
102+
template&lt;<i>constexpr-param</i> T, <i>constexpr-param</i> R&gt;
103+
constexpr auto operator/=(this T, R) noexcept
104+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x /= R::value<ins>&gt;()</ins>; }
105+
{ return constant_wrapper&lt;[] { auto v = T::value; return v /= R::value; }()&gt;{}; }
106+
template&lt;<i>constexpr-param</i> T, <i>constexpr-param</i> R&gt;
107+
constexpr auto operator%=(this T, R) noexcept
108+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x %= R::value<ins>&gt;()</ins>; }
109+
{ return constant_wrapper&lt;[] { auto v = T::value; return v %= R::value; }()&gt;{}; }
110+
template&lt;<i>constexpr-param</i> T, <i>constexpr-param</i> R&gt;
111+
constexpr auto operator&amp;=(this T, R) noexcept
112+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x &amp;= R::value<ins>&gt;()</ins>; }
113+
{ return constant_wrapper&lt;[] { auto v = T::value; return v &amp;= R::value; }()&gt;{}; }
114+
template&lt;<i>constexpr-param</i> T, <i>constexpr-param</i> R&gt;
115+
constexpr auto operator|=(this T, R) noexcept
116+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x |= R::value<ins>&gt;()</ins>; }
117+
{ return constant_wrapper&lt;[] { auto v = T::value; return v |= R::value; }()&gt;{}; }
118+
template&lt;<i>constexpr-param</i> T, <i>constexpr-param</i> R&gt;
119+
constexpr auto operator^=(this T, R) noexcept
120+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x ^= R::value<ins>&gt;()</ins>; }
121+
{ return constant_wrapper&lt;[] { auto v = T::value; return v ^= R::value; }()&gt;{}; }
122+
template&lt;<i>constexpr-param</i> T, <i>constexpr-param</i> R&gt;
123+
constexpr auto operator&lt;&lt;=(this T, R) noexcept
124+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x &lt;&lt;= R::value<ins>&gt;()</ins>; }
125+
{ return constant_wrapper&lt;[] { auto v = T::value; return v &lt;&lt;= R::value; }()&gt;{}; }
126+
template&lt;<i>constexpr-param</i> T, <i>constexpr-param</i> R&gt;
127+
constexpr auto operator&gt;&gt;=(this T, R) noexcept
128+
requires requires(T::value_type x) { <ins>constant_wrapper&lt;</ins>x &gt;&gt;= R::value<ins>&gt;()</ins>; }
129+
{ return constant_wrapper&lt;[] { auto v = T::value; return v &gt;&gt;= R::value; }()&gt;{}; }
130+
};
131+
</pre>
132+
</blockquote>
133+
134+
</li>
135+
136+
</ol>
137+
</resolution>
138+
139+
</issue>

0 commit comments

Comments
 (0)