[PATCH v2] libstdc++: Implement LWG4222 'expected' constructor from a single value missing a constraint
Patrick Palka
ppalka@redhat.com
Fri Aug 15 19:58:51 GMT 2025
On Thu, 14 Aug 2025, Yihan Wang wrote:
> libstdc++-v3/ChangeLog:
>
> * include/std/expected: Add missing constraint as per LWG 4222.
> * testsuite/20_util/expected/lwg4222.cc: New test.
>
> Signed-off-by: Yihan Wang <yronglin777@gmail.com>
> ---
> libstdc++-v3/include/std/expected | 1 +
> .../testsuite/20_util/expected/lwg4222.cc | 32 +++++++++++++++++++
> 2 files changed, 33 insertions(+)
> create mode 100644 libstdc++-v3/testsuite/20_util/expected/lwg4222.cc
>
> diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected
> index 60f1565f15b..4eaaab693e1 100644
> --- a/libstdc++-v3/include/std/expected
> +++ b/libstdc++-v3/include/std/expected
> @@ -474,6 +474,7 @@ namespace __expected
> template<typename _Up = remove_cv_t<_Tp>>
> requires (!is_same_v<remove_cvref_t<_Up>, expected>)
> && (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
> + && (!is_same_v<remove_cvref_t<_Up>, unexpect_t>)
> && is_constructible_v<_Tp, _Up>
> && (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
> && __expected::__not_constructing_bool_from_expected<_Tp, _Up>
> diff --git a/libstdc++-v3/testsuite/20_util/expected/lwg4222.cc b/libstdc++-v3/testsuite/20_util/expected/lwg4222.cc
> new file mode 100644
> index 00000000000..2483afba853
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/expected/lwg4222.cc
> @@ -0,0 +1,32 @@
> +// { dg-do compile { target c++23 } }
> +
> +// LWG 4222. 'expected' constructor from a single value missing a constraint
> +
> +#include <expected>
> +#include <type_traits>
> +#include <testsuite_hooks.h>
> +
> +struct T {
> + explicit T(auto) {}
> +};
> +struct E {
> + E(int) {}
> +};
> +
> +struct V {
> + explicit V(std::unexpect_t) {}
> +};
> +
> +static_assert(!std::is_constructible_v<std::expected<T, E>, std::unexpect_t>);
> +static_assert(!std::is_constructible_v<std::expected<T, E>, std::unexpect_t &>);
> +static_assert(!std::is_constructible_v<std::expected<T, E>, std::unexpect_t &&>);
> +static_assert(!std::is_constructible_v<std::expected<T, E>, const std::unexpect_t>);
> +static_assert(!std::is_constructible_v<std::expected<T, E>, const std::unexpect_t &>);
> +static_assert(!std::is_constructible_v<std::expected<T, E>, const std::unexpect_t &&>);
> +
> +void test() {
> + std::expected<V, int> e1(std::in_place, std::unexpect);
> + VERIFY( e1.has_value() );
> + std::expected<int, V> e2(std::unexpect, std::unexpect);
> + VERIFY( !e2.has_value() );
These VERIFY asserts aren't actually checked because this test file is
specified as a compile-only test via 'dg-do compile'.
We can either turn this into a runnable test by specifying 'dg-do run'
(and defining main()), or we can keep it a compile-only test and make
test() constexpr and add something like static_assert(test()).
(In theory constexpr tests are stronger than runtime tests since they
diagnose UB, so I generally prefer the latter. Ideally we should have
both a runtime and constexpr test but that seems overkill here.)
> +}
> --
> 2.39.5
>
>
More information about the Libstdc++
mailing list