On Tue, Jul 15, 2025 at 5:51 AM Patrick Palka <ppa...@redhat.com> wrote:

> Tested on x86_64-pc-linux-gnu, does this look OK for trunk and perhaps
> 15?  Not sure if this corner case is worth backporting any further.
>
> Can we just use direct-list-initialization via {} instead of '= T()'
> here?  I wasn't sure so I went with the latter to more closely mirror
> the standard.
>
default_initializable (
https://eel.is/c++draft/concept.default.init#concept:default_initializable)
implies that T{} is well-formed, however there is no semantic
requirements that T() and T{} are the same.

The only case when {} could break, and "= T()" would work, is an aggregate
that has
members with explicit default constructor, but it would be ill-formed.
There is also difference
between lifetime of reference members with default initializers (binding to
temporary), but
it does not matter for members of the class, as they go away with
constructors.

There is also, pre-C++20 case, where classes with deleted default
constructor, where initializable
via {} but not = T(). But again, default_initializable requires both.

So, as far I can tell if we require T being default_initializable, we can
use {} for initialization, but
given that aggregate initialization/defintion changes every standard (you
can now mix base and
designated init in C++26), I would just use = T().



> -- >8 --
>
> Data members of type __maybe_present_t where the conditionally present
> type might be an aggregate or fundamental type need to be explicitly
> value-initialized (rather than implicitly default-initialized) to ensure
> that default-initialization of the containing class results in an
> completely initialized object.
>
>         PR libstdc++/119962
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/ranges (join_view::_Iterator::_M_outer): Initialize.
>         (lazy_split_view::_OuterIter::_M_current): Initialize.
>         (join_with_view::_Iterator::_M_outer_it): Initialize.
>         * testsuite/std/ranges/adaptors/join.cc (test15): New test.
>         * testsuite/std/ranges/adaptors/join_with/1.cc (test05): New test.
>         * testsuite/std/ranges/adaptors/lazy_split.cc (test13): New test.
> ---
>  libstdc++-v3/include/std/ranges                          | 9 ++++++---
>  libstdc++-v3/testsuite/std/ranges/adaptors/join.cc       | 8 ++++++++
>  .../testsuite/std/ranges/adaptors/join_with/1.cc         | 8 ++++++++
>  libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc | 8 ++++++++
>  4 files changed, 30 insertions(+), 3 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/ranges
> b/libstdc++-v3/include/std/ranges
> index 3a6710bd0ae1..efe62969d657 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -3022,7 +3022,8 @@ namespace views::__adaptor
>           { _M_satisfy(); }
>
>           [[no_unique_address]]
> -           __detail::__maybe_present_t<forward_range<_Base>, _Outer_iter>
> _M_outer;
> +           __detail::__maybe_present_t<forward_range<_Base>, _Outer_iter>
> _M_outer
> +             = decltype(_M_outer)();
>           optional<_Inner_iter> _M_inner;
>           _Parent* _M_parent = nullptr;
>
> @@ -3376,7 +3377,8 @@ namespace views::__adaptor
>
>           [[no_unique_address]]
>             __detail::__maybe_present_t<forward_range<_Vp>,
> -                                       iterator_t<_Base>> _M_current;
> +                                       iterator_t<_Base>> _M_current
> +             = decltype(_M_current)();
>           bool _M_trailing_empty = false;
>
>         public:
> @@ -7400,7 +7402,8 @@ namespace views::__adaptor
>
>      _Parent* _M_parent = nullptr;
>      [[no_unique_address]]
> -      __detail::__maybe_present_t<forward_range<_Base>, _OuterIter>
> _M_outer_it;
> +      __detail::__maybe_present_t<forward_range<_Base>, _OuterIter>
> _M_outer_it
> +       = decltype(_M_outer_it)();
>      variant<_PatternIter, _InnerIter> _M_inner_it;
>
>      constexpr _OuterIter&
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> index 2861115c22a0..a9395b489919 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> @@ -233,6 +233,13 @@ test14()
>    VERIFY( ranges::equal(v | views::join, (int[]){1, 2, 3}) );
>  }
>
> +void
> +test15()
> +{
> +  // PR libstdc++/119962 - __maybe_present_t misses initialization
> +  constexpr
> decltype(views::join(views::single(views::single(0))).begin()) it;
> +}
> +
>  int
>  main()
>  {
> @@ -250,4 +257,5 @@ main()
>    test12();
>    test13();
>    test14();
> +  test15();
>  }
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join_with/1.cc
> b/libstdc++-v3/testsuite/std/ranges/adaptors/join_with/1.cc
> index 8ab30a5277da..4d55c9d3be78 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/join_with/1.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join_with/1.cc
> @@ -94,6 +94,13 @@ test04()
>    return true;
>  }
>
> +void
> +test05()
> +{
> +  // PR libstdc++/119962 - __maybe_present_t misses initialization
> +  constexpr decltype(views::join_with(views::single(views::single(0)),
> 0).begin()) it;
> +}
> +
>  int
>  main()
>  {
> @@ -105,4 +112,5 @@ main()
>  #else
>    VERIFY(test04());
>  #endif
> +  test05();
>  }
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> index 81fc60b362a8..321ae271bf2b 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> @@ -232,6 +232,13 @@ test12()
>    return true;
>  }
>
> +void
> +test13()
> +{
> +  // PR libstdc++/119962 - __maybe_present_t misses initialization
> +  constexpr decltype(views::lazy_split(views::single(0), 0).begin()) it;
> +}
> +
>  int
>  main()
>  {
> @@ -247,4 +254,5 @@ main()
>    test10();
>    test11();
>    static_assert(test12());
> +  test13();
>  }
> --
> 2.50.1.271.gd30e120486
>
>

Reply via email to