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
>