On Thu, 2 Apr 2026 at 11:43, Tomasz Kamiński <[email protected]> wrote:
>
> This paper removes the try_append_range from inplace_vector and changes the
> return type of try_emplace_back and try_push_back to optional<_Tp&> per:
> P4022R0: Remove try_append_range from inplace_vector for now.
> P3981R2: Better return types in std::inplace_vector and 
> std::exception_ptr_cast.
>
> The test are also expanded to cover type Y, that is convertible to 
> optional<Y&>,
> and require return type to be constructed using in_place.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/version.def (inplace_vector): Bump to 202603.
>         * include/bits/version.h: Regenerate.
>         * include/std/inplace_vector (inplace_vector::try_emplace_back)
>         (inplace_vector::try_push_back): Change return type to optional<_Tp&>
>         and adjust implementation accordingly.
>         (inplace_vector::try_append_range): Remove.
>         * include/debug/inplace_vector (inplace_vector::try_emplace_back)
>         (inplace_vector::try_push_back, inplace_vector::try_append_range):
>         Likewise.
>         * testsuite/23_containers/inplace_vector/modifiers/single_insert.cc:
>         Updated check for the optional<T&> return. Added test for type
>         convertible to optional<T&>
>         * 
> testsuite/23_containers/inplace_vector/debug/invalidation/try_emplace_back.cc:
>         Use has_value() to check if engaged optional is returned.
>         * 
> testsuite/23_containers/inplace_vector/debug/invalidation/try_push_back.cc:
>         Likewise.
>         * testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc:
>         Remove try_append_range tests.
>         * 
> testsuite/23_containers/inplace_vector/debug/invalidation/try_append_range.cc:
>         Removed.
>         * testsuite/23_containers/inplace_vector/version.cc: Updated expected
>         feature test macro value.
> ---
> The exception_ptr change need to be handled seprately, due it being
> defined in libsup++.
>
> Testing on x86_64-linux. All *inplace_vector* test already passed.
> OK for trunk if all test finishes?
>
>  libstdc++-v3/include/bits/version.def         |  5 +-
>  libstdc++-v3/include/bits/version.h           |  4 +-
>  libstdc++-v3/include/debug/inplace_vector     | 26 ++-----
>  libstdc++-v3/include/std/inplace_vector       | 70 +++++--------------
>  .../debug/invalidation/try_append_range.cc    | 45 ------------
>  .../debug/invalidation/try_emplace_back.cc    |  2 +-
>  .../debug/invalidation/try_push_back.cc       |  4 +-
>  .../inplace_vector/modifiers/multi_insert.cc  | 46 ------------
>  .../inplace_vector/modifiers/single_insert.cc | 41 ++++++++---
>  .../23_containers/inplace_vector/version.cc   |  4 +-
>  10 files changed, 66 insertions(+), 181 deletions(-)
>  delete mode 100644 
> libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_append_range.cc
>
> diff --git a/libstdc++-v3/include/bits/version.def 
> b/libstdc++-v3/include/bits/version.def
> index ada8da88bc5..998223306e2 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -2223,9 +2223,12 @@ ftms = {
>  };
>
>  ftms = {
> +  // 202406 P0843R14 inplace_vector
> +  // 202603 P3981R2 Better return types in std::inplace_vector and 
> std::exception_ptr_cast
> +  //        P4022R0 Remove try_append_range from inplace_vector for now
>    name = inplace_vector;
>    values = {
> -    v = 202406;
> +    v = 202603;
>      cxxmin = 26;
>    };
>  };
> diff --git a/libstdc++-v3/include/bits/version.h 
> b/libstdc++-v3/include/bits/version.h
> index 0490e79dd41..cb3b9b0997e 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -2488,9 +2488,9 @@
>
>  #if !defined(__cpp_lib_inplace_vector)
>  # if (__cplusplus >  202302L)
> -#  define __glibcxx_inplace_vector 202406L
> +#  define __glibcxx_inplace_vector 202603L
>  #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_inplace_vector)
> -#   define __cpp_lib_inplace_vector 202406L
> +#   define __cpp_lib_inplace_vector 202603L
>  #  endif
>  # endif
>  #endif /* !defined(__cpp_lib_inplace_vector) */
> diff --git a/libstdc++-v3/include/debug/inplace_vector 
> b/libstdc++-v3/include/debug/inplace_vector
> index 750b0a7343c..e579e8636f9 100644
> --- a/libstdc++-v3/include/debug/inplace_vector
> +++ b/libstdc++-v3/include/debug/inplace_vector
> @@ -338,11 +338,12 @@ namespace __debug
>        }
>
>        template<typename... _Args>
> -       constexpr _Tp*
> +       constexpr optional<_Tp&>
>         try_emplace_back(_Args&&... __args)
>         {
>           auto __end = _Base::cend();
> -         _Tp* __res = 
> _Base::try_emplace_back(std::forward<_Args>(__args)...);
> +         optional<_Tp&> __res
> +           = _Base::try_emplace_back(std::forward<_Args>(__args)...);

Is it valuable to state the type explicitly, rather than just using auto?

I don't have a preference, so OK for trunk either way.

>
>           if (__res)
>             this->_M_invalidate_if(_Equal(__end));
> @@ -350,11 +351,11 @@ namespace __debug
>           return __res;
>         }
>
> -      constexpr _Tp*
> +      constexpr optional<_Tp&>
>        try_push_back(const _Tp& __x)
>        {
>         const auto __end = _Base::cend();
> -       _Tp* __res = _Base::try_push_back(__x);
> +       optional<_Tp&> __res = _Base::try_push_back(__x);
>
>         if (__res)
>           this->_M_invalidate_if(_Equal(__end));
> @@ -362,11 +363,11 @@ namespace __debug
>         return __res;
>        }
>
> -      constexpr _Tp*
> +      constexpr optional<_Tp&>
>        try_push_back(_Tp&& __x)
>        {
>         const auto __end = _Base::cend();
> -       _Tp* __res = _Base::try_push_back(std::move(__x));
> +       optional<_Tp&> __res = _Base::try_push_back(std::move(__x));
>
>         if (__res)
>           this->_M_invalidate_if(_Equal(__end));
> @@ -374,19 +375,6 @@ namespace __debug
>         return __res;
>        }
>
> -      template<__detail::__container_compatible_range<_Tp> _Rg>
> -       constexpr ranges::borrowed_iterator_t<_Rg>
> -       try_append_range(_Rg&& __rg)
> -       {
> -         const auto __size = size();
> -         const auto __end = _Base::cend();
> -         auto __res = _Base::try_append_range(__rg);
> -         if (size() != __size)
> -           this->_M_invalidate_if(_Equal(__end));
> -
> -         return __res;
> -       }
> -
>        template<typename... _Args>
>         constexpr _Tp&
>         unchecked_emplace_back(_Args&&... __args)
> diff --git a/libstdc++-v3/include/std/inplace_vector 
> b/libstdc++-v3/include/std/inplace_vector
> index c22a9e1f9b4..5ad92332a02 100644
> --- a/libstdc++-v3/include/std/inplace_vector
> +++ b/libstdc++-v3/include/std/inplace_vector
> @@ -38,6 +38,7 @@
>  #ifdef __glibcxx_inplace_vector // C++ >= 26
>  #include <compare>
>  #include <initializer_list>
> +#include <optional>
>  #include <bits/stdexcept_throw.h>
>  #include <bits/range_access.h>
>  #include <bits/ranges_base.h> // borrowed_iterator_t, 
> __detail::__container_compatible_range
> @@ -550,63 +551,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>        }
>
>        template<typename... _Args>
> -       constexpr _Tp*
> +       constexpr optional<_Tp&>
>         try_emplace_back(_Args&&... __args)
>         {
>           if (_M_size >= _Nm) [[unlikely]]
> -           return nullptr;
> -         auto& __r = unchecked_emplace_back(std::forward<_Args>(__args)...);
> -         return __builtin_addressof(__r);
> +           return nullopt;
> +         return optional<_Tp&>(in_place,
> +                  unchecked_emplace_back(std::forward<_Args>(__args)...));
>         }
>
> -      constexpr _Tp*
> +      constexpr optional<_Tp&>
>        try_push_back(const _Tp& __x)
>        {
>         if (_M_size >= _Nm) [[unlikely]]
> -         return nullptr;
> -       return __builtin_addressof(unchecked_emplace_back(__x));
> +         return nullopt;
> +       return optional<_Tp&>(in_place, unchecked_emplace_back(__x));
>        }
>
> -      constexpr _Tp*
> +      constexpr optional<_Tp&>
>        try_push_back(_Tp&& __x)
>        {
>         if (_M_size >= _Nm) [[unlikely]]
> -         return nullptr;
> -       return __builtin_addressof(unchecked_emplace_back(std::move(__x)));
> +         return nullopt;
> +       return optional<_Tp&>(in_place, 
> unchecked_emplace_back(std::move(__x)));
>        }
>
> -      template<__detail::__container_compatible_range<_Tp> _Rg>
> -       constexpr ranges::borrowed_iterator_t<_Rg>
> -       try_append_range(_Rg&& __rg)
> -       {
> -         if constexpr (ranges::sized_range<_Rg>)
> -           {
> -             auto __n = ranges::distance(__rg);
> -             if (__n == 0) [[unlikely]]
> -               return ranges::begin(__rg);
> -
> -             const auto __end = data() + _M_size;
> -             const size_t __avail = _Nm - size();
> -             if (__n <= __avail)
> -               _M_size += size_type(__n);
> -             else
> -               {
> -                 __n = __avail;
> -                 _M_size = _Nm;
> -               }
> -             return ranges::uninitialized_copy_n(
> -                      ranges::begin(__rg), __n,
> -                      __end, unreachable_sentinel).in;
> -           }
> -         else
> -           {
> -             ranges::subrange<pointer> __tail(data() + _M_size, data() + 
> _Nm);
> -             auto [__in, __out] = ranges::uninitialized_copy(__rg, __tail);
> -             _M_size = __out - data();
> -             return std::move(__in);
> -           }
> -       }
> -
>        template<typename... _Args>
>         constexpr _Tp&
>         unchecked_emplace_back(_Args&&... __args)
> @@ -1208,22 +1177,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>        { __builtin_trap(); }
>
>        template<typename... _Args>
> -       constexpr _Tp*
> +        constexpr optional<_Tp&>
>         try_emplace_back(_Args&&...)
> -       { return nullptr; }
> +       { return nullopt; }
>
> -      constexpr _Tp*
> +      constexpr optional<_Tp&>
>        try_push_back(const _Tp&)
> -      { return nullptr; }
> +      { return nullopt; }
>
> -      constexpr _Tp*
> +      constexpr optional<_Tp&>
>        try_push_back(_Tp&&)
> -      { return nullptr; }
> -
> -      template<__detail::__container_compatible_range<_Tp> _Rg>
> -       constexpr ranges::borrowed_iterator_t<_Rg>
> -       try_append_range(_Rg&& __rg)
> -       { return ranges::begin(__rg); }
> +      { return nullopt; }
>
>        template<typename... _Args>
>         [[noreturn]]
> diff --git 
> a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_append_range.cc
>  
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_append_range.cc
> deleted file mode 100644
> index ae4ac418f50..00000000000
> --- 
> a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_append_range.cc
> +++ /dev/null
> @@ -1,45 +0,0 @@
> -// { dg-do run { target c++26 } }
> -
> -#include <debug/inplace_vector>
> -#include <testsuite_hooks.h>
> -
> -using __gnu_debug::inplace_vector;
> -
> -void test01()
> -{
> -  inplace_vector<int, 100> v(10, 17);
> -  inplace_vector<int, 10> v1(10, 19);
> -
> -  auto before = v.begin() + 6;
> -  auto last = v.end();
> -  auto end = last--;
> -
> -  v.try_append_range(v1);
> -
> -  VERIFY(before._M_dereferenceable());
> -  VERIFY(last._M_dereferenceable());
> -  VERIFY(end._M_singular());
> -}
> -
> -void test02()
> -{
> -  inplace_vector<int, 100> v(10, 17);
> -  inplace_vector<int, 0> v1;
> -
> -  auto before = v.begin() + 6;
> -  auto last = v.end();
> -  auto end = last--;
> -
> -  v.try_append_range(v1);
> -
> -  VERIFY(before._M_dereferenceable());
> -  VERIFY(last._M_dereferenceable());
> -  VERIFY(!end._M_singular());
> -}
> -
> -int main()
> -{
> -  test01();
> -  test02();
> -  return 0;
> -}
> diff --git 
> a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_emplace_back.cc
>  
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_emplace_back.cc
> index f7c8c7a9ba2..606d478a0dc 100644
> --- 
> a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_emplace_back.cc
> +++ 
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_emplace_back.cc
> @@ -13,7 +13,7 @@ void test01()
>    auto last = v.end();
>    auto end = last--;
>
> -  VERIFY( v.try_emplace_back(42) != nullptr );
> +  VERIFY( v.try_emplace_back(42).has_value() );
>
>    VERIFY(before._M_dereferenceable());
>    VERIFY(last._M_dereferenceable());
> diff --git 
> a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_push_back.cc
>  
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_push_back.cc
> index 04fc010e500..96af2b65b72 100644
> --- 
> a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_push_back.cc
> +++ 
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_push_back.cc
> @@ -14,7 +14,7 @@ void test01()
>    auto last = v.end();
>    auto end = last--;
>
> -  VERIFY( v.try_push_back(42) != nullptr );
> +  VERIFY( v.try_push_back(42).has_value() );
>
>    VERIFY(before._M_dereferenceable());
>    VERIFY(last._M_dereferenceable());
> @@ -30,7 +30,7 @@ void test02()
>    auto last = v.end();
>    auto end = last--;
>
> -  VERIFY( v.try_push_back(std::move(vv)) != nullptr );
> +  VERIFY( v.try_push_back(std::move(vv)).has_value() );
>
>    VERIFY(before._M_dereferenceable());
>    VERIFY(last._M_dereferenceable());
> diff --git 
> a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc
>  
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc
> index 454163892ad..e5a482cdc65 100644
> --- 
> a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc
> +++ 
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc
> @@ -54,16 +54,6 @@ test_add_to_full_it()
>
>    std::inplace_vector<T, N> v(std::from_range, std::span(a, a+N));
>
> -  Range r1(a, a);
> -  auto rit1 = v.try_append_range(r1);
> -  VERIFY( eq<T>(v, {a, N}) );
> -  VERIFY( rit1.base() == a );
> -
> -  SizedRange r2(a, a);
> -  auto rit2 = v.try_append_range(r2);
> -  VERIFY( eq<T>(v, {a, N}) );
> -  VERIFY( rit2.base() == a );
> -
>    v.append_range(Range(a, a));
>    VERIFY( eq<T>(v, {a, N}) );
>    v.append_range(SizedRange(a, a));
> @@ -93,17 +83,6 @@ test_add_to_full_it()
>    VERIFY( eq<T>(v, {a, N}) );
>    VERIFY( it == v.begin() );
>
> -  // Inserting non-empty range
> -  Range r3(a+3, a+5);
> -  auto rit3 = v.try_append_range(r3);
> -  VERIFY( eq<T>(v, {a, N}) );
> -  VERIFY( rit3.base() == a+3 );
> -
> -  SizedRange r4(a+2, a+5);
> -  auto rit4 = v.try_append_range(r4);
> -  VERIFY( eq<T>(v, {a, N}) );
> -  VERIFY( rit4.base() == a+2 );
> -
>  #ifdef __cpp_exceptions
>  #ifndef __cpp_lib_constexpr_exceptions
>    if consteval {
> @@ -265,30 +244,6 @@ test_append_range()
>  #endif
>  }
>
> -template<typename Range>
> -constexpr void
> -test_try_append_range()
> -{
> -  using T = std::ranges::range_value_t<Range>;
> -  T a[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25};
> -
> -  std::inplace_vector<T, 20> v;
> -  Range r1 = Range(a, a+10);
> -  auto it1 = v.try_append_range(r1);
> -  VERIFY( eq<T>(v, {a, 10}) );
> -  VERIFY( it1.base() == a+10 );
> -
> -  Range r2 = Range(a+10, a+15);
> -  auto it2 = v.try_append_range(r2);
> -  VERIFY( eq<T>(v, {a, 15}) );
> -  VERIFY( it2.base() == a+15 );
> -
> -  Range r3 = Range(a+15, a+25);
> -  auto it3 = v.try_append_range(r3);
> -  VERIFY( eq<T>(v, {a, 20}) );
> -  VERIFY( it3.base() == a+20 );
> -}
> -
>  template<typename Range>
>  constexpr void
>  test_insert_range()
> @@ -357,7 +312,6 @@ constexpr void
>  do_test_ranges()
>  {
>    test_append_range<Range>();
> -  test_try_append_range<Range>();
>    test_insert_range<Range>();
>  }
>
> diff --git 
> a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/single_insert.cc
>  
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/single_insert.cc
> index 92bd765982e..245af928800 100644
> --- 
> a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/single_insert.cc
> +++ 
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/single_insert.cc
> @@ -3,6 +3,7 @@
>  #include <inplace_vector>
>
>  #include <span>
> +#include <optional>
>  #include <testsuite_hooks.h>
>
>  struct X
> @@ -18,6 +19,20 @@ struct X
>    friend auto operator<=>(const X&, const X&) = default;
>  };
>
> +// Type that is convertible to optional<Y&>
> +struct Y
> +{
> +  Y() = default;
> +  constexpr Y(int p) : v(p) {}
> +
> +  constexpr operator std::optional<Y&>()
> +  { return std::nullopt; }
> +
> +  int v;
> +
> +  friend auto operator<=>(const Y&, const Y&) = default;
> +};
> +
>  template<typename T, typename V, size_t N>
>  constexpr bool
>  eq(const std::inplace_vector<V, N>& l, std::span<const T> r) {
> @@ -40,11 +55,11 @@ test_add_to_full()
>
>    std::inplace_vector<T, N> v(std::from_range, std::span(a, a+N));
>
> -  VERIFY( v.try_emplace_back(1) == nullptr );
> +  VERIFY( !v.try_emplace_back(1).has_value() );
>    VERIFY( eq<T>(v, {a, N}) );
> -  VERIFY( v.try_push_back(T(1)) == nullptr );
> +  VERIFY( !v.try_push_back(T(1)).has_value() );
>    VERIFY( eq<T>(v, {a, N}) );
> -  VERIFY( v.try_push_back(c) == nullptr );
> +  VERIFY( !v.try_push_back(c).has_value() );
>    VERIFY( eq<T>(v, {a, N}) );
>
>  #ifdef __cpp_exceptions
> @@ -149,15 +164,18 @@ test_inserts()
>    v.unchecked_push_back(c);
>    VERIFY( eq<T>(v, {a+3, 6}) );
>
> -  T* ptr = v.try_emplace_back(7);
> +  std::optional<T&> opt = v.try_emplace_back(7);
>    VERIFY( eq<T>(v, {a+3, 7}) );
> -  VERIFY( ptr = &v.back() );
> -  ptr = v.try_push_back(T(8));
> +  VERIFY( opt.has_value() );
> +  VERIFY( &*opt == &v.back() );
> +  opt = v.try_push_back(T(8));
>    VERIFY( eq<T>(v, {a+3, 8}) );
> -  VERIFY( ptr = &v.back() );
> -  ptr = v.try_push_back(c);
> +  VERIFY( opt.has_value() );
> +  VERIFY( &*opt == &v.back() );
> +  opt = v.try_push_back(c);
>    VERIFY( eq<T>(v, {a+3, 9}) );
> -  VERIFY( ptr = &v.back() );
> +  VERIFY( opt.has_value() );
> +  VERIFY( &*opt == &v.back() );
>
>    auto it = v.emplace(v.end(), 10);
>    VERIFY( eq<T>(v, {a+3, 10}) );
> @@ -195,12 +213,15 @@ int main()
>    auto test_all = [] {
>      test_add_to_full<0, int>();
>      test_add_to_full<0, X>();
> +    test_add_to_full<0, Y>();
>
>      test_add_to_full<4, int>();
> +    test_add_to_full<4, Y>();
>
>      test_inserts<int>();
> +    test_inserts<Y>();
>  #ifdef __cpp_lib_constexpr_inplace_vector
> -#error uncomemnt test_inserts<X>()
> +#error enable tests bellow
>  #endif
>      if ! consteval {
>        test_add_to_full<4, X>();
> diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/version.cc 
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/version.cc
> index a5bbdb8464b..493e86dc6dd 100644
> --- a/libstdc++-v3/testsuite/23_containers/inplace_vector/version.cc
> +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/version.cc
> @@ -5,7 +5,7 @@
>
>  #ifndef __cpp_lib_inplace_vector
>  # error "Feature-test macro for inplace_vector missing in <inplace_vector>"
> -#elif __cpp_lib_inplace_vector != 202406L
> +#elif __cpp_lib_inplace_vector != 202603L
>  # error "Feature-test macro for inplace_vector has wrong value in 
> <inplace_vector>"
>  #endif
>
> @@ -15,6 +15,6 @@
>
>  #ifndef __cpp_lib_inplace_vector
>  # error "Feature-test macro for inplace_vector missing in <version>"
> -#elif __cpp_lib_inplace_vector != 202406L
> +#elif __cpp_lib_inplace_vector != 202603L
>  # error "Feature-test macro for inplace_vector has wrong value in <version>"
>  #endif
> --
> 2.53.0
>

Reply via email to