Skip to content

Commit 1a760d5

Browse files
committed
New issue from Bronek Kozicki: "expected constructor from a single value missing a constraint"
1 parent 4112244 commit 1a760d5

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

xml/issue4222.xml

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='no'?>
2+
<!DOCTYPE issue SYSTEM "lwg-issue.dtd">
3+
4+
<issue num="4222" status="New">
5+
<title> `expected` constructor from a single value missing a constraint</title>
6+
<section>
7+
<sref ref="[expected.object.cons]"/>
8+
</section>
9+
<submitter>Bronek Kozicki</submitter>
10+
<date>06 Mar 2025</date>
11+
<priority>99</priority>
12+
13+
<discussion>
14+
<p>
15+
When an `expected` object is initialized with a constructor taking first parameter of type `unexpect_t` ,
16+
the expectation is that the object will be always initialized in disengaged state (i.e. the user expected
17+
postcondition is that `has_value()` will be `false`), as in the example:
18+
</p>
19+
<blockquote><pre>
20+
struct T { explicit T(auto) {} };
21+
struct E { E() {} };
22+
23+
int main() {
24+
expected&lt;T, E&gt; a(unexpect);
25+
assert(!a.has_value());
26+
}
27+
</pre></blockquote>
28+
<p>
29+
This does not hold when both value type `T` and error type `E` have certain properties. Observe:
30+
</p>
31+
<blockquote><pre>
32+
struct T { explicit T(auto) {} };
33+
struct E { E(int) {} }; // <span style="color:red;font-weight:bolder">Only this line changed from the above example</span>
34+
35+
int main() {
36+
expected&lt;T, E&gt; a(unexpect);
37+
assert(!a.has_value()); // <span style="color:red;font-weight:bolder">This assert will now fail</span>
38+
}
39+
</pre></blockquote>
40+
<p>
41+
In the example above the overload resolution of `a` finds the universal single parameter constructor for
42+
initializing `expected` in engaged state (<sref ref="[expected.object.cons]"/> p23):
43+
</p>
44+
<blockquote><pre>
45+
template&lt;class U = remove_cv_t&lt;T&gt;&gt;
46+
constexpr explicit(!is_convertible_v&lt;U, T&gt;) expected(U&amp;&amp; v);
47+
</pre></blockquote>
48+
<p>
49+
This constructor has a list of constraints which does not mention `unexpect_t` (but it mentions e.g. `unexpected` and
50+
`in_place_t`). Email exchange with the author of `expected` confirmed that it is an omission.
51+
<p/>
52+
The proposed resolution is to add the following additional constraint to this constructor:
53+
</p>
54+
<blockquote><p>
55+
<ins><tt>is_same_v&lt;remove_cvref_t&lt;U&gt;, unexpect_t&gt;</tt> is `false`</ins>
56+
</p></blockquote>
57+
<p>
58+
This will result in the above, most likely buggy, program to become ill-formed. If the user intent was for the object
59+
to be constructed in an engaged state, passing `unexpect_t` to the `T` constructor, they can fix the compilation error
60+
like so:
61+
</p>
62+
<blockquote><pre>
63+
expected&lt;T, E&gt; a(in_place, unexpect);
64+
</pre></blockquote>
65+
</discussion>
66+
67+
<resolution>
68+
<p>
69+
This wording is relative to <paper num="N5001"/>.
70+
</p>
71+
72+
<ol>
73+
74+
<li><p>Modify <sref ref="[expected.object.cons]"/> as indicated:</p>
75+
76+
<blockquote>
77+
<pre>
78+
template&lt;class U = remove_cv_t&lt;T&gt;&gt;
79+
constexpr explicit(!is_convertible_v&lt;U, T&gt;) expected(U&amp;&amp; v);
80+
</pre>
81+
<blockquote>
82+
<p>
83+
-23- <i>Constraints</i>:
84+
</p>
85+
<ol style="list-style-type: none">
86+
<li><p>(23.1) &mdash; <tt>is_same_v&lt;remove_cvref_t&lt;U&gt;, in_place_t&gt;</tt> is `false`; and</p></li>
87+
<li><p>(23.2) &mdash; <tt>is_same_v&lt;expected, remove_cvref_t&lt;U&gt;&gt;</tt> is `false`; and</p></li>
88+
<li><p><ins>(23.?) &mdash; <tt>is_same_v&lt;remove_cvref_t&lt;U&gt;, unexpect_t&gt;</tt> is `false`; and</ins></p></li>
89+
<li><p>(23.3) &mdash; <tt>remove_cvref_t&lt;U&gt;</tt> is not a specialization of `unexpected`; and</p></li>
90+
<li><p>(23.4) &mdash; <tt>is_constructible_v&lt;T, U&gt;</tt> is `true`; and</p></li>
91+
<li><p>(23.5) &mdash; if `T` is <i>cv</i> `bool`, <tt>remove_cvref_t&lt;U&gt;</tt> is not a specialization of `expected`.</p></li>
92+
</ol>
93+
<p>
94+
-24- <i>Effects</i>: Direct-non-list-initializes <tt><i>val</i></tt> with <tt>std::forward&lt;U&gt;(v)</tt>.
95+
<p/>
96+
-25- <i>Postconditions</i>: `has_value()` is `true`.
97+
<p/>
98+
-26- <i>Throws</i>: Any exception thrown by the initialization of <tt><i>val</i></tt>.
99+
</p>
100+
</blockquote>
101+
</blockquote>
102+
</li>
103+
</ol>
104+
</resolution>
105+
106+
</issue>

0 commit comments

Comments
 (0)