@@ -314,6 +314,17 @@ namespace __expected
__guard.release();
}
}
+
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 3836. std::expected<bool, E1> conversion constructor
+ // expected(const expected<U, G>&) should take precedence over
+ // expected(U&&) with operator bool
+
+ // If T is cv bool, remove_cvref_t<U> is not a specialization of expected.
+ template<typename _Tp, typename _Up>
+ concept __not_constructing_bool_from_expected
+ = ! is_same_v<remove_cv_t<_Tp>, bool>
+ || ! __is_expected<remove_cvref_t<_Up>>;
}
/// @endcond
@@ -327,26 +338,41 @@ namespace __expected
static_assert( ! __expected::__is_unexpected<remove_cv_t<_Tp>> );
static_assert( __expected::__can_be_unexpected<_Er> );
- template<typename _Up, typename _Err, typename _Unex = unexpected<_Er>>
+ // If T is not cv bool, converts-from-any-cvref<T, expected<U, G>> and
+ // is_constructible<unexpected<E>, cv expected<U, G> ref-qual> are false.
+ template<typename _Up, typename _Gr, typename _Unex = unexpected<_Er>,
+ typename = remove_cv_t<_Tp>>
static constexpr bool __cons_from_expected
- = __or_v<is_constructible<_Tp, expected<_Up, _Err>&>,
- is_constructible<_Tp, expected<_Up, _Err>>,
- is_constructible<_Tp, const expected<_Up, _Err>&>,
- is_constructible<_Tp, const expected<_Up, _Err>>,
- is_convertible<expected<_Up, _Err>&, _Tp>,
- is_convertible<expected<_Up, _Err>, _Tp>,
- is_convertible<const expected<_Up, _Err>&, _Tp>,
- is_convertible<const expected<_Up, _Err>, _Tp>,
- is_constructible<_Unex, expected<_Up, _Err>&>,
- is_constructible<_Unex, expected<_Up, _Err>>,
- is_constructible<_Unex, const expected<_Up, _Err>&>,
- is_constructible<_Unex, const expected<_Up, _Err>>
+ = __or_v<is_constructible<_Tp, expected<_Up, _Gr>&>,
+ is_constructible<_Tp, expected<_Up, _Gr>>,
+ is_constructible<_Tp, const expected<_Up, _Gr>&>,
+ is_constructible<_Tp, const expected<_Up, _Gr>>,
+ is_convertible<expected<_Up, _Gr>&, _Tp>,
+ is_convertible<expected<_Up, _Gr>, _Tp>,
+ is_convertible<const expected<_Up, _Gr>&, _Tp>,
+ is_convertible<const expected<_Up, _Gr>, _Tp>,
+ is_constructible<_Unex, expected<_Up, _Gr>&>,
+ is_constructible<_Unex, expected<_Up, _Gr>>,
+ is_constructible<_Unex, const expected<_Up, _Gr>&>,
+ is_constructible<_Unex, const expected<_Up, _Gr>>
>;
- template<typename _Up, typename _Err>
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // If t is cv bool, we know it can be constructed from expected<U, G>,
+ // but we don't want to cause the expected(U&&) constructor to be used,
+ // so we only check the is_constructible<unexpected<E>, ...> cases.
+ template<typename _Up, typename _Gr, typename _Unex>
+ static constexpr bool __cons_from_expected<_Up, _Gr, _Unex, bool>
+ = __or_v<is_constructible<_Unex, expected<_Up, _Gr>&>,
+ is_constructible<_Unex, expected<_Up, _Gr>>,
+ is_constructible<_Unex, const expected<_Up, _Gr>&>,
+ is_constructible<_Unex, const expected<_Up, _Gr>>
+ >;
+
+ template<typename _Up, typename _Gr>
constexpr static bool __explicit_conv
= __or_v<__not_<is_convertible<_Up, _Tp>>,
- __not_<is_convertible<_Err, _Er>>
+ __not_<is_convertible<_Gr, _Er>>
>;
template<typename _Up>
@@ -445,8 +471,9 @@ namespace __expected
template<typename _Up = _Tp>
requires (!is_same_v<remove_cvref_t<_Up>, expected>)
&& (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
- && (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
&& is_constructible_v<_Tp, _Up>
+ && (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
+ && __expected::__not_constructing_bool_from_expected<_Tp, _Up>
constexpr explicit(!is_convertible_v<_Up, _Tp>)
expected(_Up&& __v)
noexcept(is_nothrow_constructible_v<_Tp, _Up>)
new file mode 100644
@@ -0,0 +1,34 @@
+// { dg-do run { target c++23 } }
+
+#include <expected>
+#include <testsuite_hooks.h>
+
+constexpr void
+test_convert_contained_value_to_bool()
+{
+ struct BaseError { };
+ struct DerivedError : BaseError { };
+
+ std::expected<bool, DerivedError> e = false;
+
+ // Should use expected(const expected<U, G>&) ctor, not expected(U&&):
+ std::expected<bool, BaseError> e2 = e;
+
+ // Contained value should be e.value() not static_cast<bool>(e):
+ VERIFY( e2.value() == false );
+
+ std::expected<bool, DerivedError> e3(std::unexpect);
+ std::expected<const bool, BaseError> e4 = e3;
+ // Should have error, not static_cast<bool>(e3):
+ VERIFY( ! e4.has_value() );
+}
+
+int main()
+{
+ test_convert_contained_value_to_bool();
+
+ static_assert([] {
+ test_convert_contained_value_to_bool();
+ return true;
+ }());
+}