<div dir="ltr"><div dir="ltr"><div>For title say "LWG 4406 and LWG 3424", and update description paragraph,</div><div>to clarify which issue paragraph applies to which.</div><div><br></div><div>There is also suggestions to remove "else" in expected, otherwise the patch LGTM.</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Nov 20, 2025 at 6:02 PM Jonathan Wakely <<a href="mailto:jwakely@redhat.com" target="_blank">jwakely@redhat.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">This adjusts the return statements of optional::value_or and<br>
expected::value_or to not perform explicit conversions, so that the<br>
actual conversion performed matches the requirements expressed in the<br>
Mandates: elements.<br>
<br>
Also adjust the return types to remove cv-qualifiers (LWG 3424).<br>
<br>
libstdc++-v3/ChangeLog:<br>
<br>
* include/std/expected (expected::value_or): Use remove_cv_t for<br>
the return type. Do not use static_cast for return statement.<br>
Adjust static_assert conditions to match return statements.<br>
* include/std/optional (optional::value_or): Likewise.<br>
(optional<T&>::value_or): Likewise.<br>
---<br>
<br>
Tested x86_64-linux.<br>
<br>
libstdc++-v3/include/std/expected | 18 +++++++++++-------<br>
libstdc++-v3/include/std/optional | 27 +++++++++++++++++----------<br>
2 files changed, 28 insertions(+), 17 deletions(-)<br>
<br>
diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected<br>
index 4eaaab693e1e..591fc72a4388 100644<br>
--- a/libstdc++-v3/include/std/expected<br>
+++ b/libstdc++-v3/include/std/expected<br>
@@ -822,32 +822,36 @@ namespace __expected<br>
return std::move(_M_unex);<br>
}<br>
<br>
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS<br>
+ // 4406. value_or return statement is inconsistent with Mandates<br>
template<typename _Up = remove_cv_t<_Tp>><br>
- constexpr _Tp<br>
+ constexpr remove_cv_t<_Tp><br>
value_or(_Up&& __v) const &<br>
noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,<br>
is_nothrow_convertible<_Up, _Tp>>)<br>
{<br>
- static_assert( is_copy_constructible_v<_Tp> );<br>
+ using _Xp = remove_cv_t<_Tp>;<br>
+ static_assert( is_convertible_v<const _Tp&, _Xp> );<br>
static_assert( is_convertible_v<_Up, _Tp> );<br>
<br>
if (_M_has_value)<br>
return _M_val;<br>
- return static_cast<_Tp>(std::forward<_Up>(__v));<br>
+ return std::forward<_Up>(__v);<br>
}<br>
<br>
template<typename _Up = remove_cv_t<_Tp>><br>
- constexpr _Tp<br>
+ constexpr remove_cv_t<_Tp><br>
value_or(_Up&& __v) &&<br>
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,<br>
is_nothrow_convertible<_Up, _Tp>>)<br>
{<br>
- static_assert( is_move_constructible_v<_Tp> );<br>
- static_assert( is_convertible_v<_Up, _Tp> );<br>
+ using _Xp = remove_cv_t<_Tp>;<br>
+ static_assert( is_convertible_v<_Tp, _Xp> );<br>
+ static_assert( is_convertible_v<_Up, _Xp> );<br>
<br>
if (_M_has_value)<br>
return std::move(_M_val);<br>
- return static_cast<_Tp>(std::forward<_Up>(__v));<br>
+ return std::forward<_Up>(__v);<br>
}<br>
<br>
template<typename _Gr = _Er><br>
diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional<br>
index 41c04b10720e..7d5af1936617 100644<br>
--- a/libstdc++-v3/include/std/optional<br>
+++ b/libstdc++-v3/include/std/optional<br>
@@ -1285,30 +1285,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION<br>
__throw_bad_optional_access();<br>
}<br>
<br>
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS<br>
+ // 4406. value_or return statement is inconsistent with Mandates<br>
template<typename _Up = remove_cv_t<_Tp>><br>
- constexpr _Tp<br>
+ constexpr remove_cv_t<_Tp><br>
value_or(_Up&& __u) const&<br>
{<br>
- static_assert(is_copy_constructible_v<_Tp>);<br>
- static_assert(is_convertible_v<_Up&&, _Tp>);<br>
+ using _Xp = remove_cv_t<_Tp>;<br>
+ static_assert(is_convertible_v<const _Tp&, _Xp>);<br>
+ static_assert(is_convertible_v<_Up, _Xp>);<br>
<br>
if (this->_M_is_engaged())<br>
return this->_M_get();<br>
else<br></blockquote><div>Optional and standard does not use this else (it is not needed),</div><div>so I would also remove it here for consistency. </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
- return static_cast<_Tp>(std::forward<_Up>(__u));<br>
+ return std::forward<_Up>(__u);<br>
}<br>
<br>
template<typename _Up = remove_cv_t<_Tp>><br>
- constexpr _Tp<br>
+ constexpr remove_cv_t<_Tp><br>
value_or(_Up&& __u) &&<br>
{<br>
- static_assert(is_move_constructible_v<_Tp>);<br>
- static_assert(is_convertible_v<_Up&&, _Tp>);<br>
+ using _Xp = remove_cv_t<_Tp>;<br>
+ static_assert(is_convertible_v<_Tp, _Xp>);<br>
+ static_assert(is_convertible_v<_Up, _Xp>);<br>
<br>
if (this->_M_is_engaged())<br>
return std::move(this->_M_get());<br>
else<br></blockquote><div>Same here. </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
- return static_cast<_Tp>(std::forward<_Up>(__u));<br>
+ return std::forward<_Up>(__u);<br>
}<br>
<br>
#if __cpp_lib_optional >= 202110L // C++23<br>
@@ -1726,9 +1730,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION<br>
value_or(_Up&& __u) const<br>
{<br>
using _Xp = remove_cv_t<_Tp>;<br>
- static_assert(is_constructible_v<_Xp, _Tp&>);<br>
+ static_assert(is_convertible_v<_Tp&, _Xp>);<br>
static_assert(is_convertible_v<_Up, _Xp>);<br>
- return _M_val ? *_M_val : static_cast<_Xp>(std::forward<_Up>(__u));<br>
+ if (_M_val)<br>
+ return *_M_val; </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+ else<br></blockquote><div>Sane here. </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+ return std::forward<_Up>(__u);<br>
}<br>
<br>
// Monadic operations.<br>
-- <br>
2.51.1<br>
<br>
</blockquote></div></div>
</div>