public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r15-5910] libstdc++: Add fancy pointer support to std::forward_list [PR57272]
@ 2024-12-03 21:36 Jonathan Wakely
0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2024-12-03 21:36 UTC (permalink / raw)
To: gcc-cvs, libstdc++-cvs
https://gcc.gnu.org/g:84b40a1c1b2c9e3feb546838fa988d653eed0755
commit r15-5910-g84b40a1c1b2c9e3feb546838fa988d653eed0755
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Fri Nov 15 21:45:16 2024 +0000
libstdc++: Add fancy pointer support to std::forward_list [PR57272]
This takes a very similar approach to the changes for std::list.
libstdc++-v3/ChangeLog:
PR libstdc++/57272
* include/bits/forward_list.h (_GLIBCXX_USE_ALLOC_PTR_FOR_LIST):
Define.
(_Fwd_list_node_base::_M_base_ptr): New member functions.
(_Fwd_list_node::_M_node_ptr): New member function.
(_Fwd_list_iterator, _Fwd_list_const_iterator): Make internal
member functions and data member private. Declare forward_list
and _Fwd_list_base as friends.
(__fwdlist::_Node_base, __fwdlist::_Node, __fwdlist::_Iterator):
New class templates.
(__fwdlist::_Node_traits): New class template.
(_Fwd_list_base): Use _Node_traits to get types. Use _Base_ptr
instad of _Fwd_list_node_base*. Use _M_base_ptr() instead of
taking address of head node.
(forward_list): Likewise.
(_Fwd_list_base::_M_get_node): Do not define for versioned
namespace.
(_Fwd_list_base::_M_put_node): Only convert pointer if needed.
(_Fwd_list_base::_M_create_node): Use __allocate_guarded_obj.
(_Fwd_list_base::_M_destroy_node): New member function.
* include/bits/forward_list.tcc (_Fwd_list_base::_M_insert_after)
(forward_list::_M_splice_after, forward_list::insert_after): Use
const_iterator::_M_const_cast() instead of casting pointers.
(_Fwd_list_base::_M_erase_after): Use _M_destroy_node.
(forward_list::remove, forward_list::remove_if): Only do
downcasts when accessing the value.
(forward_list::sort): Likewise.
* testsuite/23_containers/forward_list/capacity/1.cc: Check
max_size for new node type.
* testsuite/23_containers/forward_list/capacity/node_sizes.cc:
New test.
* testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr.cc:
New test.
* testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr_ignored.cc:
New test.
Diff:
---
libstdc++-v3/include/bits/forward_list.h | 508 +++++++++++++++++----
libstdc++-v3/include/bits/forward_list.tcc | 124 ++---
.../23_containers/forward_list/capacity/1.cc | 11 +-
.../forward_list/capacity/node_sizes.cc | 24 +
.../forward_list/requirements/completeness.cc | 19 +
.../explicit_instantiation/alloc_ptr.cc | 88 ++++
.../explicit_instantiation/alloc_ptr_ignored.cc | 4 +
7 files changed, 635 insertions(+), 143 deletions(-)
diff --git a/libstdc++-v3/include/bits/forward_list.h b/libstdc++-v3/include/bits/forward_list.h
index f6d4e6bd3d26..f4a53bb4d7ef 100644
--- a/libstdc++-v3/include/bits/forward_list.h
+++ b/libstdc++-v3/include/bits/forward_list.h
@@ -40,6 +40,9 @@
#include <bits/stl_algobase.h>
#include <bits/stl_function.h>
#include <bits/allocator.h>
+#include <bits/allocated_ptr.h>
+#include <bits/ptr_traits.h>
+#include <debug/assertions.h>
#include <ext/alloc_traits.h>
#include <ext/aligned_buffer.h>
#include <debug/assertions.h>
@@ -48,6 +51,10 @@
# include <bits/ranges_util.h> // ranges::subrange
#endif
+#if ! defined _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+# define _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST 1
+#endif
+
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -55,11 +62,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
/**
* @brief A helper basic node class for %forward_list.
+ *
* This is just a linked list with nothing inside it.
* There are purely list shuffling utility methods here.
*/
struct _Fwd_list_node_base
{
+ using _Base_ptr = _Fwd_list_node_base*;
+
_Fwd_list_node_base() = default;
_Fwd_list_node_base(_Fwd_list_node_base&& __x) noexcept
: _M_next(__x._M_next)
@@ -108,6 +118,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
_M_next->_M_next = __keep;
}
}
+
+ _Fwd_list_node_base* _M_base_ptr() { return this; }
+ const _Fwd_list_node_base* _M_base_ptr() const { return this; }
};
/**
@@ -120,6 +133,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
struct _Fwd_list_node
: public _Fwd_list_node_base
{
+ using _Node_ptr = _Fwd_list_node*;
+
_Fwd_list_node() = default;
__gnu_cxx::__aligned_buffer<_Tp> _M_storage;
@@ -131,8 +146,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
const _Tp*
_M_valptr() const noexcept
{ return _M_storage._M_ptr(); }
+
+ _Node_ptr
+ _M_node_ptr()
+ { return this; }
};
+ template<typename _Tp> struct _Fwd_list_const_iterator;
+
/**
* @brief A forward_list::iterator.
*
@@ -200,6 +221,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{ return __x._M_node != __y._M_node; }
#endif
+ private:
+ template<typename, typename>
+ friend class forward_list;
+ template<typename, typename>
+ friend struct _Fwd_list_base;
+ friend struct _Fwd_list_const_iterator<_Tp>;
+
_Self
_M_next() const noexcept
{
@@ -283,6 +311,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{ return __x._M_node != __y._M_node; }
#endif
+ private:
+ template<typename, typename>
+ friend class forward_list;
+ template<typename, typename>
+ friend struct _Fwd_list_base;
+
_Self
_M_next() const noexcept
{
@@ -292,23 +326,295 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return _Fwd_list_const_iterator(nullptr);
}
+ _Fwd_list_iterator<_Tp>
+ _M_const_cast() const noexcept
+ {
+ return _Fwd_list_iterator<_Tp>(
+ const_cast<_Fwd_list_node_base*>(_M_node));
+ }
+
const _Fwd_list_node_base* _M_node;
};
+ template<typename _Tp, typename _Allocator> class forward_list;
+ template<typename _Tp, typename _Allocator> struct _Fwd_list_base;
+
+namespace __fwdlist
+{
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+ /// The node-base type for allocators that use fancy pointers.
+ template<typename _VoidPtr>
+ struct _Node_base
+ {
+ using _Base_ptr = __ptr_rebind<_VoidPtr, _Node_base>;
+
+ _Node_base() = default;
+
+ _Node_base(_Node_base&& __x) noexcept
+ : _M_next(__x._M_next)
+ { __x._M_next = nullptr; }
+
+ _Node_base(const _Node_base&) = delete;
+ _Node_base& operator=(const _Node_base&) = delete;
+
+ _Node_base&
+ operator=(_Node_base&& __x) noexcept
+ {
+ _M_next = __x._M_next;
+ __x._M_next = nullptr;
+ return *this;
+ }
+
+ _Base_ptr _M_next = nullptr;
+
+ // Splice (begin,end) before _M_next.
+ _Base_ptr
+ _M_transfer_after(_Base_ptr __begin, _Base_ptr __end) noexcept
+ {
+ _Base_ptr __keep = __begin->_M_next;
+ if (__end)
+ {
+ __begin->_M_next = __end->_M_next;
+ __end->_M_next = _M_next;
+ }
+ else
+ __begin->_M_next = nullptr;
+ _M_next = __keep;
+ return __end;
+ }
+
+ void
+ _M_reverse_after() noexcept
+ {
+ _Base_ptr __tail = _M_next;
+ if (!__tail)
+ return;
+ while (_Base_ptr __temp = __tail->_M_next)
+ {
+ _Base_ptr __keep = _M_next;
+ _M_next = __temp;
+ __tail->_M_next = __temp->_M_next;
+ _M_next->_M_next = __keep;
+ }
+ }
+
+ // This is not const-correct, but it's only used in a const access path
+ // by std::forward_list::empty(), where it doesn't escape, and by
+ // std::forward_list::before_begin() const, where the pointer is used
+ // to initialize a const_iterator and so constness is restored.
+ _Base_ptr
+ _M_base_ptr() const
+ {
+ return pointer_traits<_Base_ptr>::
+ pointer_to(const_cast<_Node_base&>(*this));
+ }
+ };
+
+ /**
+ * @brief A helper node class for %forward_list.
+ */
+ template<typename _ValPtr>
+ struct _Node
+ : public _Node_base<__ptr_rebind<_ValPtr, void>>
+ {
+ using value_type = typename pointer_traits<_ValPtr>::element_type;
+ using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
+
+ _Node() { }
+ ~_Node() { }
+ _Node(_Node&&) = delete;
+
+ union {
+#if ! _GLIBCXX_INLINE_VERSION
+ // For ABI compatibility we need to overalign this member.
+ alignas(__alignof__(value_type)) // XXX GLIBCXX_ABI Deprecated
+#endif
+ value_type _M_data;
+ };
+
+ value_type*
+ _M_valptr() noexcept
+ { return std::__addressof(_M_data); }
+
+ const value_type*
+ _M_valptr() const noexcept
+ { return std::__addressof(_M_data); }
+
+ _Node_ptr
+ _M_node_ptr()
+ { return pointer_traits<_Node_ptr>::pointer_to(*this); }
+ };
+
+ /// A forward_list iterator when the allocator uses fancy pointers.
+ template<bool _Const, typename _Ptr>
+ class _Iterator
+ {
+ using _Node = __fwdlist::_Node<_Ptr>;
+ using _Base_ptr
+ = typename __fwdlist::_Node_base<__ptr_rebind<_Ptr, void>>::_Base_ptr;
+
+ template<typename _Tp>
+ using __maybe_const = __conditional_t<_Const, const _Tp, _Tp>;
+
+ public:
+ using value_type = typename pointer_traits<_Ptr>::element_type;
+ using difference_type = ptrdiff_t;
+ using iterator_category = forward_iterator_tag;
+ using pointer = __maybe_const<value_type>*;
+ using reference = __maybe_const<value_type>&;
+
+ constexpr _Iterator() noexcept : _M_node() { }
+
+ _Iterator(const _Iterator&) = default;
+ _Iterator& operator=(const _Iterator&) = default;
+
+#ifdef __glibcxx_concepts
+ constexpr
+ _Iterator(const _Iterator<false, _Ptr>& __i) requires _Const
+#else
+ template<bool _OtherConst,
+ typename = __enable_if_t<_Const && !_OtherConst>>
+ constexpr
+ _Iterator(const _Iterator<_OtherConst, _Ptr>& __i)
+#endif
+ : _M_node(__i._M_node) { }
+
+ constexpr explicit
+ _Iterator(_Base_ptr __x) noexcept
+ : _M_node(__x) { }
+
+ [[__nodiscard__]]
+ constexpr reference
+ operator*() const noexcept
+ { return static_cast<_Node&>(*this->_M_node)._M_data; }
+
+ [[__nodiscard__]]
+ constexpr pointer
+ operator->() const noexcept
+ { return static_cast<_Node&>(*this->_M_node)._M_valptr(); }
+
+ _GLIBCXX14_CONSTEXPR _Iterator&
+ operator++() noexcept
+ {
+ _M_node = _M_node->_M_next;
+ return *this;
+ }
+
+ _GLIBCXX14_CONSTEXPR _Iterator
+ operator++(int) noexcept
+ {
+ _Iterator __tmp(*this);
+ _M_node = _M_node->_M_next;
+ return __tmp;
+ }
+
+ /**
+ * @brief Forward list iterator equality comparison.
+ */
+ [[__nodiscard__]]
+ friend constexpr bool
+ operator==(const _Iterator& __x, const _Iterator& __y) noexcept
+ { return __x._M_node == __y._M_node; }
+
+#if __cpp_impl_three_way_comparison < 201907L
+ /**
+ * @brief Forward list iterator inequality comparison.
+ */
+ [[__nodiscard__]]
+ friend constexpr bool
+ operator!=(const _Iterator& __x, const _Iterator& __y) noexcept
+ { return __x._M_node != __y._M_node; }
+#endif
+
+ private:
+ template<typename _Tp, typename _Allocator>
+ friend class _GLIBCXX_STD_C::forward_list;
+ template<typename _Tp, typename _Allocator>
+ friend struct _GLIBCXX_STD_C::_Fwd_list_base;
+
+ constexpr _Iterator<false, _Ptr>
+ _M_const_cast() const noexcept
+ { return _Iterator<false, _Ptr>(_M_node); }
+
+ friend _Iterator<!_Const, _Ptr>;
+
+ constexpr _Iterator
+ _M_next() const noexcept
+ { return _Iterator(_M_node ? _M_node->_M_next : nullptr); }
+
+ _Base_ptr _M_node;
+ };
+#endif // USE_ALLOC_PTR_FOR_FWD_LIST
+
+ // Determine the node and iterator types used by std::forward_list.
+ template<typename _Tp, typename _Ptr>
+ struct _Node_traits;
+
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_LIST <= 9000
+ // Specialization for the simple case where the allocator's pointer type
+ // is the same type as value_type*.
+ // For ABI compatibility we can't change the types used for this case.
+ template<typename _Tp>
+ struct _Node_traits<_Tp, _Tp*>
+ {
+ using _Node_base = _Fwd_list_node_base;
+ using _Node = _Fwd_list_node<_Tp>;
+ using _Iterator = _Fwd_list_iterator<_Tp>;
+ using _Const_iterator = _Fwd_list_const_iterator<_Tp>;
+ };
+#endif
+
+#if ! _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+ // Always use the T* specialization.
+ template<typename _Tp, typename _Ptr>
+ struct _Node_traits
+ : _Node_traits<_Tp, _Tp*>
+ { };
+#else
+ // Primary template used when the allocator uses fancy pointers.
+ template<typename _Tp, typename _Ptr>
+ struct _Node_traits
+ {
+ private:
+ using _VoidPtr = __ptr_rebind<_Ptr, void>;
+ using _ValPtr = __ptr_rebind<_Ptr, _Tp>;
+
+ public:
+ using _Node_base = __fwdlist::_Node_base<_VoidPtr>;
+ using _Node = __fwdlist::_Node<_ValPtr>;
+ using _Iterator = __fwdlist::_Iterator<false, _ValPtr>;
+ using _Const_iterator = __fwdlist::_Iterator<true, _ValPtr>;
+ };
+#endif // USE_ALLOC_PTR_FOR_FWD_LIST
+} // namespace __fwdlist
+
/**
* @brief Base class for %forward_list.
*/
template<typename _Tp, typename _Alloc>
struct _Fwd_list_base
{
+#if __cplusplus > 201703L || defined __STRICT_ANSI__
+ // The static_assert in forward_list ensures _Alloc::value_type is _Tp.
+ using pointer = typename allocator_traits<_Alloc>::pointer;
+#else
+ using _Tp_alloc_traits
+ = typename allocator_traits<_Alloc>::template rebind_traits<_Tp>;
+ using pointer = typename _Tp_alloc_traits::pointer;
+#endif
+
protected:
- typedef __alloc_rebind<_Alloc, _Fwd_list_node<_Tp>> _Node_alloc_type;
- typedef __gnu_cxx::__alloc_traits<_Node_alloc_type> _Node_alloc_traits;
+ using _Node_traits = __fwdlist::_Node_traits<_Tp, pointer>;
+ using _Node = typename _Node_traits::_Node;
+ using _Node_alloc_type = __alloc_rebind<_Alloc, _Node>;
+ using _Node_alloc_traits = __gnu_cxx::__alloc_traits<_Node_alloc_type>;
+ using _Node_ptr = typename _Node_alloc_traits::pointer;
+ using _Base_ptr = typename _Node_traits::_Node_base::_Base_ptr;
struct _Fwd_list_impl
: public _Node_alloc_type
{
- _Fwd_list_node_base _M_head;
+ typename _Node_traits::_Node_base _M_head;
_Fwd_list_impl()
noexcept(is_nothrow_default_constructible<_Node_alloc_type>::value)
@@ -329,9 +635,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
_Fwd_list_impl _M_impl;
public:
- typedef _Fwd_list_iterator<_Tp> iterator;
- typedef _Fwd_list_const_iterator<_Tp> const_iterator;
- typedef _Fwd_list_node<_Tp> _Node;
+ using iterator = typename _Node_traits::_Iterator;
+ using const_iterator = typename _Node_traits::_Const_iterator;
_Node_alloc_type&
_M_get_Node_allocator() noexcept
@@ -358,54 +663,71 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
_Fwd_list_base(_Fwd_list_base&&) = default;
~_Fwd_list_base()
- { _M_erase_after(&_M_impl._M_head, nullptr); }
+ { _M_erase_after(_M_impl._M_head._M_base_ptr(), nullptr); }
protected:
+#if ! _GLIBCXX_INLINE_VERSION
+ // XXX GLIBCXX_ABI Deprecated
_Node*
_M_get_node()
{
auto __ptr = _Node_alloc_traits::allocate(_M_get_Node_allocator(), 1);
return std::__to_address(__ptr);
}
+#endif
+
+ void
+ _M_put_node(_Node_ptr __p)
+ {
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+ _Node_alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1);
+#else
+ typedef typename _Node_alloc_traits::pointer _Ptr;
+ auto __ptr = std::pointer_traits<_Ptr>::pointer_to(*__p);
+ _Node_alloc_traits::deallocate(_M_get_Node_allocator(), __ptr, 1);
+#endif
+ }
template<typename... _Args>
- _Node*
+ _Node_ptr
_M_create_node(_Args&&... __args)
{
- _Node* __node = this->_M_get_node();
- __try
- {
- ::new ((void*)__node) _Node;
- _Node_alloc_traits::construct(_M_get_Node_allocator(),
- __node->_M_valptr(),
- std::forward<_Args>(__args)...);
- }
- __catch(...)
- {
- this->_M_put_node(__node);
- __throw_exception_again;
- }
- return __node;
+ auto& __alloc = _M_get_Node_allocator();
+ auto __guard = std::__allocate_guarded_obj(__alloc);
+ _Node_alloc_traits::construct(__alloc, __guard->_M_valptr(),
+ std::forward<_Args>(__args)...);
+ auto __p = __guard.release();
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+ return __p;
+#else
+ return std::__to_address(__p);
+#endif
}
- template<typename... _Args>
- _Fwd_list_node_base*
- _M_insert_after(const_iterator __pos, _Args&&... __args);
-
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
void
- _M_put_node(_Node* __p)
+ _M_destroy_node(_Node_ptr __p)
{
- typedef typename _Node_alloc_traits::pointer _Ptr;
- auto __ptr = std::pointer_traits<_Ptr>::pointer_to(*__p);
- _Node_alloc_traits::deallocate(_M_get_Node_allocator(), __ptr, 1);
+ auto& __alloc = _M_get_Node_allocator();
+ // Destroy the element
+ _Node_alloc_traits::destroy(__alloc, __p->_M_valptr());
+ // Only destroy the node if the pointers require it.
+ if constexpr (!is_trivially_destructible<_Base_ptr>::value)
+ __p->~_Node();
+ _M_put_node(__p);
}
+#pragma GCC diagnostic pop
+
+ template<typename... _Args>
+ _Base_ptr
+ _M_insert_after(const_iterator __pos, _Args&&... __args);
- _Fwd_list_node_base*
- _M_erase_after(_Fwd_list_node_base* __pos);
+ _Base_ptr
+ _M_erase_after(_Base_ptr __pos);
- _Fwd_list_node_base*
- _M_erase_after(_Fwd_list_node_base* __pos,
- _Fwd_list_node_base* __last);
+ _Base_ptr
+ _M_erase_after(_Base_ptr __pos, _Base_ptr __last);
};
/**
@@ -582,12 +904,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
forward_list(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc())
: _Base(_Node_alloc_type(__a))
{
- _Node_base* __to = &this->_M_impl._M_head;
+ auto __to = this->_M_impl._M_head._M_base_ptr();
auto __first = ranges::begin(__rg);
const auto __last = ranges::end(__rg);
for (; __first != __last; ++__first)
{
- __to->_M_next = this->_M_create_node(*__first);
+ __to->_M_next = this->_M_create_node(*__first)->_M_base_ptr();
__to = __to->_M_next;
}
}
@@ -843,7 +1165,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
[[__nodiscard__]]
iterator
before_begin() noexcept
- { return iterator(&this->_M_impl._M_head); }
+ { return iterator(this->_M_impl._M_head._M_base_ptr()); }
/**
* Returns a read-only (constant) iterator that points before the
@@ -853,7 +1175,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
[[__nodiscard__]]
const_iterator
before_begin() const noexcept
- { return const_iterator(&this->_M_impl._M_head); }
+ { return const_iterator(this->_M_impl._M_head._M_base_ptr()); }
/**
* Returns a read/write iterator that points to the first element
@@ -912,7 +1234,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
[[__nodiscard__]]
const_iterator
cbefore_begin() const noexcept
- { return const_iterator(&this->_M_impl._M_head); }
+ { return const_iterator(this->_M_impl._M_head._M_base_ptr()); }
/**
* Returns a read-only (constant) iterator that points one past
@@ -952,8 +1274,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
front()
{
__glibcxx_requires_nonempty();
- _Node* __front = static_cast<_Node*>(this->_M_impl._M_head._M_next);
- return *__front->_M_valptr();
+ _Node& __front = static_cast<_Node&>(*this->_M_impl._M_head._M_next);
+ return *__front._M_valptr();
}
/**
@@ -965,8 +1287,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
front() const
{
__glibcxx_requires_nonempty();
- _Node* __front = static_cast<_Node*>(this->_M_impl._M_head._M_next);
- return *__front->_M_valptr();
+ _Node& __front = static_cast<_Node&>(*this->_M_impl._M_head._M_next);
+ return *__front._M_valptr();
}
// 23.3.4.5 modifiers:
@@ -1059,7 +1381,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
*/
void
pop_front()
- { this->_M_erase_after(&this->_M_impl._M_head); }
+ {
+ __glibcxx_requires_nonempty();
+ this->_M_erase_after(this->_M_impl._M_head._M_base_ptr());
+ }
/**
* @brief Constructs object in %forward_list after the specified
@@ -1206,8 +1531,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
*/
iterator
erase_after(const_iterator __pos)
- { return iterator(this->_M_erase_after(const_cast<_Node_base*>
- (__pos._M_node))); }
+ { return iterator(this->_M_erase_after(__pos._M_const_cast()._M_node)); }
/**
* @brief Remove a range of elements.
@@ -1230,10 +1554,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
*/
iterator
erase_after(const_iterator __pos, const_iterator __last)
- { return iterator(this->_M_erase_after(const_cast<_Node_base*>
- (__pos._M_node),
- const_cast<_Node_base*>
- (__last._M_node))); }
+ {
+ return iterator(this->_M_erase_after(__pos._M_const_cast()._M_node,
+ __last._M_const_cast()._M_node));
+ }
/**
* @brief Swaps data with another %forward_list.
@@ -1295,7 +1619,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
*/
void
clear() noexcept
- { this->_M_erase_after(&this->_M_impl._M_head, nullptr); }
+ { this->_M_erase_after(this->_M_impl._M_head._M_base_ptr(), nullptr); }
// 23.3.4.6 forward_list operations:
@@ -1530,6 +1854,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
_M_default_insert_after(const_iterator __pos, size_type __n);
#if ! _GLIBCXX_INLINE_VERSION
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
// XXX GLIBCXX_ABI Deprecated
// These members are unused by std::forward_list now, but we keep them
// here so that an explicit instantiation will define them.
@@ -1537,53 +1863,79 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
// so that explicit instantiation declarations of std::forward_list that
// were compiled with old versions of GCC can still find these symbols.
+ // Use 'if constexpr' so that the functions don't do anything for
+ // specializations using _Node_traits<T, fancy-pointer>, because any
+ // old code referencing these symbols wasn't using the fancy-pointer
+ // specializations.
+
void
_M_move_assign(forward_list&& __list, true_type) noexcept
{
- clear();
- this->_M_impl._M_head._M_next = __list._M_impl._M_head._M_next;
- __list._M_impl._M_head._M_next = nullptr;
- std::__alloc_on_move(this->_M_get_Node_allocator(),
- __list._M_get_Node_allocator());
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+ if constexpr (is_same<typename _Alloc_traits::pointer, _Tp*>::value)
+#endif
+ {
+ clear();
+ this->_M_impl._M_head._M_next = __list._M_impl._M_head._M_next;
+ __list._M_impl._M_head._M_next = nullptr;
+ std::__alloc_on_move(this->_M_get_Node_allocator(),
+ __list._M_get_Node_allocator());
+ }
}
void
_M_move_assign(forward_list&& __list, false_type)
{
- if (__list._M_get_Node_allocator() == this->_M_get_Node_allocator())
- _M_move_assign(std::move(__list), true_type());
- else
- // The rvalue's allocator cannot be moved, or is not equal,
- // so we need to individually move each element.
- this->assign(std::make_move_iterator(__list.begin()),
- std::make_move_iterator(__list.end()));
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+ if constexpr (is_same<typename _Alloc_traits::pointer, _Tp*>::value)
+#endif
+ {
+ if (__list._M_get_Node_allocator() == this->_M_get_Node_allocator())
+ _M_move_assign(std::move(__list), true_type());
+ else
+ // The rvalue's allocator cannot be moved, or is not equal,
+ // so we need to individually move each element.
+ this->assign(std::make_move_iterator(__list.begin()),
+ std::make_move_iterator(__list.end()));
+ }
}
void
_M_assign_n(size_type __n, const _Tp& __val, true_type)
{
- auto __prev = before_begin();
- auto __curr = begin();
- auto __end = end();
- while (__curr != __end && __n > 0)
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+ if constexpr (is_same<typename _Alloc_traits::pointer, _Tp*>::value)
+#endif
{
- *__curr = __val;
- ++__prev;
- ++__curr;
- --__n;
+ auto __prev = before_begin();
+ auto __curr = begin();
+ auto __end = end();
+ while (__curr != __end && __n > 0)
+ {
+ *__curr = __val;
+ ++__prev;
+ ++__curr;
+ --__n;
+ }
+ if (__n > 0)
+ insert_after(__prev, __n, __val);
+ else if (__curr != __end)
+ erase_after(__prev, __end);
}
- if (__n > 0)
- insert_after(__prev, __n, __val);
- else if (__curr != __end)
- erase_after(__prev, __end);
}
void
_M_assign_n(size_type __n, const _Tp& __val, false_type)
{
- clear();
- insert_after(cbefore_begin(), __n, __val);
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+ if constexpr (is_same<typename _Alloc_traits::pointer, _Tp*>::value)
+#endif
+ {
+ clear();
+ insert_after(cbefore_begin(), __n, __val);
+ }
}
+#pragma GCC diagnostic pop
#endif // ! _GLIBCXX_INLINE_VERSION
};
diff --git a/libstdc++-v3/include/bits/forward_list.tcc b/libstdc++-v3/include/bits/forward_list.tcc
index 9750c7c0502b..140e9955714e 100644
--- a/libstdc++-v3/include/bits/forward_list.tcc
+++ b/libstdc++-v3/include/bits/forward_list.tcc
@@ -46,47 +46,42 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
template<typename _Tp, typename _Alloc>
template<typename... _Args>
- _Fwd_list_node_base*
+ auto
_Fwd_list_base<_Tp, _Alloc>::
_M_insert_after(const_iterator __pos, _Args&&... __args)
+ -> _Base_ptr
{
- _Fwd_list_node_base* __to
- = const_cast<_Fwd_list_node_base*>(__pos._M_node);
- _Node* __thing = _M_create_node(std::forward<_Args>(__args)...);
+ auto __to = __pos._M_const_cast()._M_node;
+ _Node_ptr __thing = _M_create_node(std::forward<_Args>(__args)...);
__thing->_M_next = __to->_M_next;
- __to->_M_next = __thing;
+ __to->_M_next = __thing->_M_base_ptr();
return __to->_M_next;
}
template<typename _Tp, typename _Alloc>
- _Fwd_list_node_base*
+ auto
_Fwd_list_base<_Tp, _Alloc>::
- _M_erase_after(_Fwd_list_node_base* __pos)
+ _M_erase_after(_Base_ptr __pos)
+ -> _Base_ptr
{
- _Node* __curr = static_cast<_Node*>(__pos->_M_next);
- __pos->_M_next = __curr->_M_next;
- _Node_alloc_traits::destroy(_M_get_Node_allocator(),
- __curr->_M_valptr());
- __curr->~_Node();
- _M_put_node(__curr);
+ auto& __curr = static_cast<_Node&>(*__pos->_M_next);
+ __pos->_M_next = __curr._M_next;
+ _M_destroy_node(__curr._M_node_ptr());
return __pos->_M_next;
}
template<typename _Tp, typename _Alloc>
- _Fwd_list_node_base*
+ auto
_Fwd_list_base<_Tp, _Alloc>::
- _M_erase_after(_Fwd_list_node_base* __pos,
- _Fwd_list_node_base* __last)
+ _M_erase_after(_Base_ptr __pos, _Base_ptr __last)
+ -> _Base_ptr
{
- _Node* __curr = static_cast<_Node*>(__pos->_M_next);
+ _Base_ptr __curr = __pos->_M_next;
while (__curr != __last)
{
- _Node* __temp = __curr;
- __curr = static_cast<_Node*>(__curr->_M_next);
- _Node_alloc_traits::destroy(_M_get_Node_allocator(),
- __temp->_M_valptr());
- __temp->~_Node();
- _M_put_node(__temp);
+ auto& __node = static_cast<_Node&>(*__curr);
+ __curr = __curr->_M_next;
+ _M_destroy_node(__node._M_node_ptr());
}
__pos->_M_next = __last;
return __last;
@@ -99,10 +94,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
forward_list<_Tp, _Alloc>::
_M_range_initialize(_InputIterator __first, _InputIterator __last)
{
- _Node_base* __to = &this->_M_impl._M_head;
+ auto __to = this->_M_impl._M_head._M_base_ptr();
for (; __first != __last; ++__first)
{
- __to->_M_next = this->_M_create_node(*__first);
+ __to->_M_next = this->_M_create_node(*__first)->_M_base_ptr();
__to = __to->_M_next;
}
}
@@ -113,10 +108,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
forward_list<_Tp, _Alloc>::
_M_fill_initialize(size_type __n, const value_type& __value)
{
- _Node_base* __to = &this->_M_impl._M_head;
+ auto __to = this->_M_impl._M_head._M_base_ptr();
for (; __n; --__n)
{
- __to->_M_next = this->_M_create_node(__value);
+ __to->_M_next = this->_M_create_node(__value)->_M_base_ptr();
__to = __to->_M_next;
}
}
@@ -126,10 +121,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
forward_list<_Tp, _Alloc>::
_M_default_initialize(size_type __n)
{
- _Node_base* __to = &this->_M_impl._M_head;
+ auto __to = this->_M_impl._M_head._M_base_ptr();
for (; __n; --__n)
{
- __to->_M_next = this->_M_create_node();
+ __to->_M_next = this->_M_create_node()->_M_base_ptr();
__to = __to->_M_next;
}
}
@@ -220,9 +215,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
_M_splice_after(const_iterator __pos,
const_iterator __before, const_iterator __last)
{
- _Node_base* __tmp = const_cast<_Node_base*>(__pos._M_node);
- _Node_base* __b = const_cast<_Node_base*>(__before._M_node);
- _Node_base* __end = __b;
+ auto __tmp = __pos._M_const_cast()._M_node;
+ auto __b = __before._M_const_cast()._M_node;
+ auto __end = __b;
while (__end && __end->_M_next != __last._M_node)
__end = __end->_M_next;
@@ -245,9 +240,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
if (__pos == __i || __pos == __j)
return;
- _Node_base* __tmp = const_cast<_Node_base*>(__pos._M_node);
- __tmp->_M_transfer_after(const_cast<_Node_base*>(__i._M_node),
- const_cast<_Node_base*>(__j._M_node));
+ auto __tmp = __pos._M_const_cast()._M_node;
+ __tmp->_M_transfer_after(__i._M_const_cast()._M_node,
+ __j._M_const_cast()._M_node);
}
template<typename _Tp, typename _Alloc>
@@ -261,7 +256,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return _M_splice_after(__pos, __tmp.before_begin(), __tmp.end());
}
else
- return iterator(const_cast<_Node_base*>(__pos._M_node));
+ return __pos._M_const_cast();
}
template<typename _Tp, typename _Alloc>
@@ -275,7 +270,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
if (!__tmp.empty())
return _M_splice_after(__pos, __tmp.before_begin(), __tmp.end());
else
- return iterator(const_cast<_Node_base*>(__pos._M_node));
+ return __pos._M_const_cast();
}
#if __cplusplus > 201703L
@@ -293,8 +288,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
forward_list __to_destroy(get_allocator());
auto __prev_it = cbefore_begin();
- while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next))
- if (*__tmp->_M_valptr() == __val)
+ while (auto __tmp = __prev_it._M_node->_M_next)
+ if (*static_cast<_Node&>(*__tmp)._M_valptr() == __val)
{
__to_destroy.splice_after(__to_destroy.cbefore_begin(),
*this, __prev_it);
@@ -316,8 +311,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
forward_list __to_destroy(get_allocator());
auto __prev_it = cbefore_begin();
- while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next))
- if (__pred(*__tmp->_M_valptr()))
+ while (auto __tmp = __prev_it._M_node->_M_next)
+ if (__pred(*static_cast<_Node&>(*__tmp)._M_valptr()))
{
__to_destroy.splice_after(__to_destroy.cbefore_begin(),
*this, __prev_it);
@@ -372,15 +367,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
if (std::__addressof(__list) == this)
return;
- _Node_base* __node = &this->_M_impl._M_head;
- while (__node->_M_next && __list._M_impl._M_head._M_next)
+ using _Base_ptr = typename _Node::_Base_ptr;
+
+ _Base_ptr __node = this->_M_impl._M_head._M_base_ptr();
+ _Base_ptr __other = __list._M_impl._M_head._M_base_ptr();
+ while (__node->_M_next && __other->_M_next)
{
- if (__comp(*static_cast<_Node*>
- (__list._M_impl._M_head._M_next)->_M_valptr(),
- *static_cast<_Node*>
- (__node->_M_next)->_M_valptr()))
- __node->_M_transfer_after(&__list._M_impl._M_head,
- __list._M_impl._M_head._M_next);
+ auto& __l = static_cast<_Node&>(*__other->_M_next);
+ auto& __r = static_cast<_Node&>(*__node->_M_next);
+ if (__comp(*__l._M_valptr(), *__r._M_valptr()))
+ __node->_M_transfer_after(__other, __other->_M_next);
__node = __node->_M_next;
}
@@ -416,18 +412,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
forward_list<_Tp, _Alloc>::
sort(_Comp __comp)
{
- // If `next' is nullptr, return immediately.
- _Node* __list = static_cast<_Node*>(this->_M_impl._M_head._M_next);
- if (!__list)
+ if (empty())
return;
+ using _Base_ptr = typename _Node::_Base_ptr;
+
+ // If `next' is nullptr, return immediately.
+ _Base_ptr __list = this->_M_impl._M_head._M_next;
+
unsigned long __insize = 1;
while (1)
{
- _Node* __p = __list;
+ _Base_ptr __p = __list;
__list = nullptr;
- _Node* __tail = nullptr;
+ _Base_ptr __tail = nullptr;
// Count number of merges we do in this pass.
unsigned long __nmerges = 0;
@@ -437,12 +436,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
++__nmerges;
// There exists a merge to be done.
// Step `insize' places along from p.
- _Node* __q = __p;
+ _Base_ptr __q = __p;
unsigned long __psize = 0;
for (unsigned long __i = 0; __i < __insize; ++__i)
{
++__psize;
- __q = static_cast<_Node*>(__q->_M_next);
+ __q = __q->_M_next;
if (!__q)
break;
}
@@ -454,33 +453,34 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
while (__psize > 0 || (__qsize > 0 && __q))
{
// Decide whether next node of merge comes from p or q.
- _Node* __e;
+ _Base_ptr __e;
if (__psize == 0)
{
// p is empty; e must come from q.
__e = __q;
- __q = static_cast<_Node*>(__q->_M_next);
+ __q = __q->_M_next;
--__qsize;
}
else if (__qsize == 0 || !__q)
{
// q is empty; e must come from p.
__e = __p;
- __p = static_cast<_Node*>(__p->_M_next);
+ __p = __p->_M_next;
--__psize;
}
- else if (!__comp(*__q->_M_valptr(), *__p->_M_valptr()))
+ else if (!__comp(*static_cast<_Node&>(*__q)._M_valptr(),
+ *static_cast<_Node&>(*__p)._M_valptr()))
{
// First node of q is not lower; e must come from p.
__e = __p;
- __p = static_cast<_Node*>(__p->_M_next);
+ __p = __p->_M_next;
--__psize;
}
else
{
// First node of q is lower; e must come from q.
__e = __q;
- __q = static_cast<_Node*>(__q->_M_next);
+ __q = __q->_M_next;
--__qsize;
}
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/capacity/1.cc b/libstdc++-v3/testsuite/23_containers/forward_list/capacity/1.cc
index 119bf6ca0274..590bba5bd254 100644
--- a/libstdc++-v3/testsuite/23_containers/forward_list/capacity/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/capacity/1.cc
@@ -36,13 +36,18 @@ test01()
VERIFY(fld.empty() == true);
#ifdef _GLIBCXX_DEBUG
- using std::_GLIBCXX_STD_C::_Fwd_list_node;
+ namespace C = std::_GLIBCXX_STD_C;
#else
- using std::_Fwd_list_node;
+ namespace C = std;
#endif
- std::allocator<_Fwd_list_node<double> > a;
+ std::allocator<C::_Fwd_list_node<double>> a;
VERIFY( fld.max_size() == __gnu_test::max_size(a) );
+
+#if _GLIBCXX_FWDLIST_USE_ALLOC_PTR
+ std::allocator<C::__fwdlist::_Node<double*>> b;
+ VERIFY( __gnu_test::max_size(b) == __gnu_test::max_size(a) );
+#endif
}
int
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/capacity/node_sizes.cc b/libstdc++-v3/testsuite/23_containers/forward_list/capacity/node_sizes.cc
new file mode 100644
index 000000000000..a709031783b6
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/capacity/node_sizes.cc
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++11 } }
+
+#include <forward_list>
+
+#if _GLIBCXX_FWDLIST_USE_ALLOC_PTR
+
+#ifdef _GLIBCXX_DEBUG
+namespace C = std::_GLIBCXX_STD_C;
+#else
+namespace C = std;
+#endif
+
+// We use double here because for ADJUST_FIELD_ALIGN targets (like i386)
+// its alignment differs when used as a data member or as a complete object.
+static_assert(sizeof(C::_Fwd_list_node<double>)
+ == sizeof(C::__fwdlist::_Node<double*>),
+ "node types have same size");
+static_assert(alignof(C::_Fwd_list_node<double>)
+ == alignof(C::__fwdlist::_Node<double*>),
+ "node types have same alignment");
+static_assert(__alignof(C::_Fwd_list_node<double>)
+ == __alignof(C::__fwdlist::_Node<double*>),
+ "node types have same preferred alignment");
+#endif
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/requirements/completeness.cc b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/completeness.cc
new file mode 100644
index 000000000000..abc9df504e23
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/completeness.cc
@@ -0,0 +1,19 @@
+// { dg-do compile {target c++11 } }
+
+// C++17 [forwardlist.overview]
+// An incomplete type T may be used when instantiating forward_list if the
+// allocator satisfies the allocator completeness requirements (20.5.3.5.1).
+// T shall be complete before any member of the resulting specialization
+// of forward_list is referenced.
+
+#include <forward_list>
+
+struct Incomplete;
+
+// This instantiates std::forward_list, but none of its members.
+const int sz = sizeof(std::forward_list<Incomplete>);
+
+// Technically the following references a member of std::forward_list,
+// but because our iterators are SCARY it doesn't instantiate any members
+// of std::forward_list.
+std::forward_list<Incomplete>::iterator i{};
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr.cc b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr.cc
new file mode 100644
index 000000000000..621ff4c652cb
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr.cc
@@ -0,0 +1,88 @@
+// { dg-do compile { target c++11 } }
+
+#include <forward_list>
+#include <testsuite_allocator.h>
+
+// An allocator that uses __gnu_cxx::_Pointer_adapter as its pointer type.
+template class std::forward_list<int, __gnu_test::CustomPointerAlloc<int>>;
+
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST != 0
+// This fancy pointer is incompatible with _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST.
+// Unlike __gnu_cxx::_Pointer_adapter, this fancy pointer supports neither
+// implicit nor explicit conversions from raw pointers. The constructor from
+// a raw pointer is explicit and requires a second parameter. The only way for
+// containers to construct one of these pointers is pointer_traits::pointer_to.
+template<typename T>
+struct Pointer : __gnu_test::PointerBase<Pointer<T>, T>
+{
+ using Base = __gnu_test::PointerBase<Pointer<T>, T>;
+
+ Pointer() = default;
+ Pointer(std::nullptr_t) : Base() { }
+ explicit Pointer(T* p, int) : Base(p) { }
+
+ // Allow conversions to const_pointer and to void_pointer
+ template<typename U, typename = typename std::enable_if<
+ (!std::is_const<U>::value && std::is_same<T, const U>::value)
+ || (std::is_void<T>::value && std::is_convertible<U*, T*>::value)
+ >::type>
+ Pointer(const Pointer<U>& p) : Base(p.operator->()) { }
+
+ template<typename U>
+ static typename std::enable_if<std::is_same<U, T>::value, Pointer>::type
+ pointer_to(U& t)
+ { return Pointer(std::addressof(t), 1); }
+};
+
+// A minimal allocator that uses Pointer as its pointer type.
+template<typename T>
+struct Allocator
+{
+ using value_type = T;
+ using pointer = Pointer<T>;
+
+ Allocator() = default;
+ template<typename U>
+ Allocator(const Allocator<U>&) { }
+
+ pointer allocate(std::size_t n)
+ { return pointer(std::allocator<T>().allocate(n), 1); }
+
+ void deallocate(pointer p, std::size_t n)
+ {
+ std::allocator<T>().deallocate(p.operator->(), n);
+ }
+
+ bool operator==(const Allocator&) const { return true; }
+ bool operator!=(const Allocator&) const { return false; }
+};
+
+template class std::forward_list<int, Allocator<int>>;
+
+#include <testsuite_iterators.h>
+
+void
+test_template_members(__gnu_test::input_container<short>& c)
+{
+ // Use member functions that are not included in explicit instantiations.
+ std::forward_list<int, Allocator<int>> l(c.begin(), c.end());
+ l.assign(c.begin(), c.end());
+ l.insert_after(l.before_begin(), c.begin(), c.end());
+ l.emplace_front(1);
+ l.emplace_after(l.before_begin(), 1);
+ l.remove_if([](int) { return false; });
+ l.unique([](int, int) { return false; });
+ l.merge(l, [](int, int) { return false; });
+ l.merge(std::move(l), [](int, int) { return false; });
+ l.sort([](int, int) { return false; });
+
+#ifdef __glibcxx_ranges_to_container
+ short arr[2];
+ __gnu_test::test_input_range<short> r(arr);
+ std::forward_list<int, Allocator<int>> l2(std::from_range, r);
+ l2.assign_range(r);
+ l2.prepend_range(r);
+ l2.insert_range_after(l2.begin(), r);
+#endif
+}
+#endif
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr_ignored.cc b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr_ignored.cc
new file mode 100644
index 000000000000..6205a2ff3bf2
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr_ignored.cc
@@ -0,0 +1,4 @@
+// { dg-options "-D_GLIBCXX_FWDLIST_USE_ALLOC_PTR=0" }
+// { dg-do compile { target c++11 } }
+
+#include "alloc_ptr.cc"
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2024-12-03 21:36 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-12-03 21:36 [gcc r15-5910] libstdc++: Add fancy pointer support to std::forward_list [PR57272] Jonathan Wakely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).