Re: std::forward_list optim for always equal allocator
On 23/11/17 22:22 +0100, François Dumont wrote: Gentle reminder for this patch. I looked when the constructor got unused and I think it is back in June 2015 in git commit: commit debb6aabb771ed02cb7256a7719555e5fbd7d3f7 Author: rediDate: Wed Jun 17 17:45:45 2015 + * include/bits/forward_list.h (_Fwd_list_base(const _Node_alloc_type&)): Change parameter to rvalue-reference. Hmm, I should have put that same change on the gcc-5-branch too. If you fear abi breaking change I can restore it in a !_GLIBCXX_INLINE_VERSION section. I think if there was a problem here my June 2015 change would already have caused it (when I changed the _Fwd_list_base constructor signatures). So let's assume it's OK to remove the constructor. @@ -533,15 +560,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER /** * @brief The %forward_list move constructor. - * @param __list A %forward_list of identical element and allocator - * types. + * @param A %forward_list of identical element and allocator types. This change is wrong, you can't just remove the parameter name, because now Doxygen will document a parameter called "A" (and complain that there is no such parameter). It would be better to leave the name __list there and just get the warning. Otherwise the patch is OK for trunk (please ensure to update the Copyright dates in the test files to 2018). Thanks.
Re: std::forward_list optim for always equal allocator
Gentle reminder for this patch. I looked when the constructor got unused and I think it is back in June 2015 in git commit: commit debb6aabb771ed02cb7256a7719555e5fbd7d3f7 Author: rediDate: Wed Jun 17 17:45:45 2015 + * include/bits/forward_list.h (_Fwd_list_base(const _Node_alloc_type&)): Change parameter to rvalue-reference. If you fear abi breaking change I can restore it in a !_GLIBCXX_INLINE_VERSION section. François On 28/08/2017 21:09, François Dumont wrote: Hi Any news for this patch ? It does remove a constructor: - _Fwd_list_impl(const _Node_alloc_type& __a) - : _Node_alloc_type(__a), _M_head() It was already unused before the patch. Do you think it has ever been used and so do I need to restore it ? I eventually restore the _M_head() in _Fwd_list_impl constructors cause IMO it is the inline init of _M_next in _Fwd_list_node_base which should be removed. But I remember Jonathan that you didn't want to do so because gcc was not good enough in detecting usage of uninitialized variables, is it still true ? François On 17/07/2017 22:10, François Dumont wrote: Hi Here is the patch to implement the always equal alloc optimization for forward_list. With this version there is no abi issue. I also prefer to implement the _Fwd_list_node_base move operator for consistency with the move constructor and used it where applicable. * include/bits/forward_list.h (_Fwd_list_node_base& operator=(_Fwd_list_node_base&&)): Implement. (_Fwd_list_impl(_Fwd_list_impl&&, _Node_alloc_type&&)): New. (_Fwd_list_base(_Fwd_list_base&&, _Node_alloc_type&&, std::true_type)): New, use latter. (forward_list(forward_list&&, _Node_alloc_type&&, std::false_type)): New. (forward_list(forward_list&&, _Node_alloc_type&&, std::true_type)): New. (forward_list(forward_list&&, const _Alloc&)): Adapt to use latters. * include/bits/forward_list.tcc (_Fwd_list_base(_Fwd_list_base&&, _Node_alloc_type&&)): Adapt to use _M_impl._M_head move assignment. (forward_list<>::merge(forward_list<>&&, _Comp)): Likewise. Tested under Linux x86_64, ok to commit ? François diff --git a/libstdc++-v3/include/bits/forward_list.h b/libstdc++-v3/include/bits/forward_list.h index 9d86fcc..772e9a0 100644 --- a/libstdc++-v3/include/bits/forward_list.h +++ b/libstdc++-v3/include/bits/forward_list.h @@ -54,6 +54,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER struct _Fwd_list_node_base { _Fwd_list_node_base() = default; +_Fwd_list_node_base(_Fwd_list_node_base&& __x) noexcept + : _M_next(__x._M_next) +{ __x._M_next = nullptr; } + +_Fwd_list_node_base(const _Fwd_list_node_base&) = delete; +_Fwd_list_node_base& operator=(const _Fwd_list_node_base&) = delete; + +_Fwd_list_node_base& +operator=(_Fwd_list_node_base&& __x) noexcept +{ + _M_next = __x._M_next; + __x._M_next = nullptr; + return *this; +} _Fwd_list_node_base* _M_next = nullptr; @@ -68,7 +82,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER __end->_M_next = _M_next; } else - __begin->_M_next = 0; + __begin->_M_next = nullptr; _M_next = __keep; return __end; } @@ -173,7 +187,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER if (_M_node) return _Fwd_list_iterator(_M_node->_M_next); else - return _Fwd_list_iterator(0); + return _Fwd_list_iterator(nullptr); } _Fwd_list_node_base* _M_node; @@ -244,7 +258,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER if (this->_M_node) return _Fwd_list_const_iterator(_M_node->_M_next); else - return _Fwd_list_const_iterator(0); + return _Fwd_list_const_iterator(nullptr); } const _Fwd_list_node_base* _M_node; @@ -285,11 +299,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _Fwd_list_node_base _M_head; _Fwd_list_impl() + noexcept( noexcept(_Node_alloc_type()) ) : _Node_alloc_type(), _M_head() { } -_Fwd_list_impl(const _Node_alloc_type& __a) -: _Node_alloc_type(__a), _M_head() + _Fwd_list_impl(_Fwd_list_impl&&) = default; + + _Fwd_list_impl(_Fwd_list_impl&& __fl, _Node_alloc_type&& __a) + : _Node_alloc_type(std::move(__a)), _M_head(std::move(__fl._M_head)) { } _Fwd_list_impl(_Node_alloc_type&& __a) @@ -312,26 +329,26 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _M_get_Node_allocator() const noexcept { return this->_M_impl; } - _Fwd_list_base() - : _M_impl() { } + _Fwd_list_base() = default; _Fwd_list_base(_Node_alloc_type&& __a) : _M_impl(std::move(__a)) { } + // When allocators are always equal. + _Fwd_list_base(_Fwd_list_base&& __lst, _Node_alloc_type&& __a, + std::true_type) + : _M_impl(std::move(__lst._M_impl), std::move(__a)) + { } + + // When allocators are not always equal. _Fwd_list_base(_Fwd_list_base&& __lst,
Re: std::forward_list optim for always equal allocator
On 12/09/2017 00:10, Jonathan Wakely wrote: On 11/09/17 22:39 +0200, Daniel Krügler wrote: 2017-09-11 22:36 GMT+02:00 François Dumont: [..] So my remark was rather for the: _Fwd_list_iterator() noexcept : _M_node() { } that could simply be _Fwd_list_iterator() = default; no ? Yes, that should be fine. I'm not sure there's much benefit to that change Sure, it would be a minor change. Which is moreover not part of this patch proposal. Is the patch ok to commit ? François
Re: std::forward_list optim for always equal allocator
On 11/09/17 22:39 +0200, Daniel Krügler wrote: 2017-09-11 22:36 GMT+02:00 François Dumont: [..] So my remark was rather for the: _Fwd_list_iterator() noexcept : _M_node() { } that could simply be _Fwd_list_iterator() = default; no ? Yes, that should be fine. I'm not sure there's much benefit to that change.
Re: std::forward_list optim for always equal allocator
2017-09-11 22:36 GMT+02:00 François Dumont: [..] > So my remark was rather for the: > > _Fwd_list_iterator() noexcept > : _M_node() { } > > that could simply be > > _Fwd_list_iterator() = default; > > no ? Yes, that should be fine. - Daniel
Re: std::forward_list optim for always equal allocator
On 11/09/2017 14:11, Jonathan Wakely wrote: On 11/09/17 07:44 +0200, Daniel Krügler wrote: 2017-09-11 7:12 GMT+02:00 François Dumont: When user declare a container iterator like that: std::forward_list::iterator it; There is no reason to initialize it with a null node pointer. It is just an uninitialized iterator which is invalid to use except to initialize it. While that is correct, for every forward iterator (and std::forward_list::iterator meets these requirements), it is also required that a value-initialized iterator can be compared against other initialized iterators, so this reduces the amount of freedom to define a default constructor for such iterators even when used to default-initialize. This is not meant as a showstopper argument, since I have not fully understood of what you are planning, but just a reminder. Right, which means that std::forward_list::iterator it = {}; must initialize the node pointer to nullptr. If we remove the initialization of _Fwd_list_iterator::_M_node from the default constructor then it would be left uninitialized. But I'm confused, François was talking about removing the initialization of _Fwd_list_node_base::_M_next, what has that got to do with forward_list::iterator? Thee is no node-base in the iterator. So I'm still wondering why the initialization of _M_next should be removed. Indeed, the iterator contains a _Fwd_list_node_base*. So my remark was rather for the: _Fwd_list_iterator() noexcept : _M_node() { } that could simply be _Fwd_list_iterator() = default; no ? François
Re: std::forward_list optim for always equal allocator
On 11/09/17 07:44 +0200, Daniel Krügler wrote: 2017-09-11 7:12 GMT+02:00 François Dumont: When user declare a container iterator like that: std::forward_list::iterator it; There is no reason to initialize it with a null node pointer. It is just an uninitialized iterator which is invalid to use except to initialize it. While that is correct, for every forward iterator (and std::forward_list::iterator meets these requirements), it is also required that a value-initialized iterator can be compared against other initialized iterators, so this reduces the amount of freedom to define a default constructor for such iterators even when used to default-initialize. This is not meant as a showstopper argument, since I have not fully understood of what you are planning, but just a reminder. Right, which means that std::forward_list::iterator it = {}; must initialize the node pointer to nullptr. If we remove the initialization of _Fwd_list_iterator::_M_node from the default constructor then it would be left uninitialized. But I'm confused, François was talking about removing the initialization of _Fwd_list_node_base::_M_next, what has that got to do with forward_list::iterator? Thee is no node-base in the iterator. So I'm still wondering why the initialization of _M_next should be removed.
Re: std::forward_list optim for always equal allocator
2017-09-11 7:12 GMT+02:00 François Dumont: > When user declare a container iterator like that: > > std::forward_list::iterator it; > > There is no reason to initialize it with a null node pointer. It is just an > uninitialized iterator which is invalid to use except to initialize it. While that is correct, for every forward iterator (and std::forward_list::iterator meets these requirements), it is also required that a value-initialized iterator can be compared against other initialized iterators, so this reduces the amount of freedom to define a default constructor for such iterators even when used to default-initialize. This is not meant as a showstopper argument, since I have not fully understood of what you are planning, but just a reminder. - Daniel
Re: std::forward_list optim for always equal allocator
On 08/09/2017 18:19, Jonathan Wakely wrote: On 28/08/17 21:09 +0200, François Dumont wrote: Hi Any news for this patch ? It does remove a constructor: -_Fwd_list_impl(const _Node_alloc_type& __a) -: _Node_alloc_type(__a), _M_head() It was already unused before the patch. Do you think it has ever been used and so do I need to restore it ? I eventually restore the _M_head() in _Fwd_list_impl constructors cause IMO it is the inline init of _M_next in _Fwd_list_node_base which should be removed. But I remember Jonathan that you didn't want to do so because gcc was not good enough in detecting usage of uninitialized variables, is it still true ? Why should it be removed? When user declare a container iterator like that: std::forward_list::iterator it; There is no reason to initialize it with a null node pointer. It is just an uninitialized iterator which is invalid to use except to initialize it. I once proposed to do the same simplification for the unordered containers _Hash_node_base. But you said that detection of the usage of uninitialized variable is not good enough in gcc to leave variables uninitialized this way.
Re: std::forward_list optim for always equal allocator
On 28/08/17 21:09 +0200, François Dumont wrote: Hi Any news for this patch ? It does remove a constructor: -_Fwd_list_impl(const _Node_alloc_type& __a) -: _Node_alloc_type(__a), _M_head() It was already unused before the patch. Do you think it has ever been used and so do I need to restore it ? I eventually restore the _M_head() in _Fwd_list_impl constructors cause IMO it is the inline init of _M_next in _Fwd_list_node_base which should be removed. But I remember Jonathan that you didn't want to do so because gcc was not good enough in detecting usage of uninitialized variables, is it still true ? Why should it be removed?
Re: std::forward_list optim for always equal allocator
Hi Any news for this patch ? It does remove a constructor: -_Fwd_list_impl(const _Node_alloc_type& __a) -: _Node_alloc_type(__a), _M_head() It was already unused before the patch. Do you think it has ever been used and so do I need to restore it ? I eventually restore the _M_head() in _Fwd_list_impl constructors cause IMO it is the inline init of _M_next in _Fwd_list_node_base which should be removed. But I remember Jonathan that you didn't want to do so because gcc was not good enough in detecting usage of uninitialized variables, is it still true ? François On 17/07/2017 22:10, François Dumont wrote: Hi Here is the patch to implement the always equal alloc optimization for forward_list. With this version there is no abi issue. I also prefer to implement the _Fwd_list_node_base move operator for consistency with the move constructor and used it where applicable. * include/bits/forward_list.h (_Fwd_list_node_base& operator=(_Fwd_list_node_base&&)): Implement. (_Fwd_list_impl(_Fwd_list_impl&&, _Node_alloc_type&&)): New. (_Fwd_list_base(_Fwd_list_base&&, _Node_alloc_type&&, std::true_type)): New, use latter. (forward_list(forward_list&&, _Node_alloc_type&&, std::false_type)): New. (forward_list(forward_list&&, _Node_alloc_type&&, std::true_type)): New. (forward_list(forward_list&&, const _Alloc&)): Adapt to use latters. * include/bits/forward_list.tcc (_Fwd_list_base(_Fwd_list_base&&, _Node_alloc_type&&)): Adapt to use _M_impl._M_head move assignment. (forward_list<>::merge(forward_list<>&&, _Comp)): Likewise. Tested under Linux x86_64, ok to commit ? François diff --git a/libstdc++-v3/include/bits/forward_list.h b/libstdc++-v3/include/bits/forward_list.h index 9d86fcc..772e9a0 100644 --- a/libstdc++-v3/include/bits/forward_list.h +++ b/libstdc++-v3/include/bits/forward_list.h @@ -54,6 +54,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER struct _Fwd_list_node_base { _Fwd_list_node_base() = default; +_Fwd_list_node_base(_Fwd_list_node_base&& __x) noexcept + : _M_next(__x._M_next) +{ __x._M_next = nullptr; } + +_Fwd_list_node_base(const _Fwd_list_node_base&) = delete; +_Fwd_list_node_base& operator=(const _Fwd_list_node_base&) = delete; + +_Fwd_list_node_base& +operator=(_Fwd_list_node_base&& __x) noexcept +{ + _M_next = __x._M_next; + __x._M_next = nullptr; + return *this; +} _Fwd_list_node_base* _M_next = nullptr; @@ -68,7 +82,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER __end->_M_next = _M_next; } else - __begin->_M_next = 0; + __begin->_M_next = nullptr; _M_next = __keep; return __end; } @@ -173,7 +187,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER if (_M_node) return _Fwd_list_iterator(_M_node->_M_next); else - return _Fwd_list_iterator(0); + return _Fwd_list_iterator(nullptr); } _Fwd_list_node_base* _M_node; @@ -244,7 +258,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER if (this->_M_node) return _Fwd_list_const_iterator(_M_node->_M_next); else - return _Fwd_list_const_iterator(0); + return _Fwd_list_const_iterator(nullptr); } const _Fwd_list_node_base* _M_node; @@ -285,11 +299,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _Fwd_list_node_base _M_head; _Fwd_list_impl() + noexcept( noexcept(_Node_alloc_type()) ) : _Node_alloc_type(), _M_head() { } -_Fwd_list_impl(const _Node_alloc_type& __a) -: _Node_alloc_type(__a), _M_head() + _Fwd_list_impl(_Fwd_list_impl&&) = default; + + _Fwd_list_impl(_Fwd_list_impl&& __fl, _Node_alloc_type&& __a) + : _Node_alloc_type(std::move(__a)), _M_head(std::move(__fl._M_head)) { } _Fwd_list_impl(_Node_alloc_type&& __a) @@ -312,26 +329,26 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _M_get_Node_allocator() const noexcept { return this->_M_impl; } - _Fwd_list_base() - : _M_impl() { } + _Fwd_list_base() = default; _Fwd_list_base(_Node_alloc_type&& __a) : _M_impl(std::move(__a)) { } + // When allocators are always equal. + _Fwd_list_base(_Fwd_list_base&& __lst, _Node_alloc_type&& __a, + std::true_type) + : _M_impl(std::move(__lst._M_impl), std::move(__a)) + { } + + // When allocators are not always equal. _Fwd_list_base(_Fwd_list_base&& __lst, _Node_alloc_type&& __a); - _Fwd_list_base(_Fwd_list_base&& __lst) - : _M_impl(std::move(__lst._M_get_Node_allocator())) - { - this->_M_impl._M_head._M_next = __lst._M_impl._M_head._M_next; - __lst._M_impl._M_head._M_next = 0; - } + _Fwd_list_base(_Fwd_list_base&&) = default; ~_Fwd_list_base() - { _M_erase_after(&_M_impl._M_head, 0); } + { _M_erase_after(&_M_impl._M_head, nullptr); } protected: - _Node* _M_get_node() { @@ -437,10 +454,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER /**
Re: std::forward_list optim for always equal allocator
2017-07-17 22:10 GMT+02:00 François Dumont: > Hi > > Here is the patch to implement the always equal alloc optimization for > forward_list. With this version there is no abi issue. > > I also prefer to implement the _Fwd_list_node_base move operator for > consistency with the move constructor and used it where applicable. > > > * include/bits/forward_list.h > (_Fwd_list_node_base& operator=(_Fwd_list_node_base&&)): Implement. > (_Fwd_list_impl(_Fwd_list_impl&&, _Node_alloc_type&&)): New. > (_Fwd_list_base(_Fwd_list_base&&, _Node_alloc_type&&, std::true_type)): > New, use latter. > (forward_list(forward_list&&, _Node_alloc_type&&, std::false_type)): > New. > (forward_list(forward_list&&, _Node_alloc_type&&, std::true_type)): > New. > (forward_list(forward_list&&, const _Alloc&)): Adapt to use latters. > * include/bits/forward_list.tcc > (_Fwd_list_base(_Fwd_list_base&&, _Node_alloc_type&&)): Adapt to use > _M_impl._M_head move assignment. > (forward_list<>::merge(forward_list<>&&, _Comp)): Likewise. > > Tested under Linux x86_64, ok to commit ? Out of curiosity: Shouldn't _Fwd_list_node_base& operator=(_Fwd_list_node_base&& __x); be declared noexcept? Thanks, - Daniel
std::forward_list optim for always equal allocator
Hi Here is the patch to implement the always equal alloc optimization for forward_list. With this version there is no abi issue. I also prefer to implement the _Fwd_list_node_base move operator for consistency with the move constructor and used it where applicable. * include/bits/forward_list.h (_Fwd_list_node_base& operator=(_Fwd_list_node_base&&)): Implement. (_Fwd_list_impl(_Fwd_list_impl&&, _Node_alloc_type&&)): New. (_Fwd_list_base(_Fwd_list_base&&, _Node_alloc_type&&, std::true_type)): New, use latter. (forward_list(forward_list&&, _Node_alloc_type&&, std::false_type)): New. (forward_list(forward_list&&, _Node_alloc_type&&, std::true_type)): New. (forward_list(forward_list&&, const _Alloc&)): Adapt to use latters. * include/bits/forward_list.tcc (_Fwd_list_base(_Fwd_list_base&&, _Node_alloc_type&&)): Adapt to use _M_impl._M_head move assignment. (forward_list<>::merge(forward_list<>&&, _Comp)): Likewise. Tested under Linux x86_64, ok to commit ? François diff --git a/libstdc++-v3/include/bits/forward_list.h b/libstdc++-v3/include/bits/forward_list.h index 9ddbcb2..dec91ea 100644 --- a/libstdc++-v3/include/bits/forward_list.h +++ b/libstdc++-v3/include/bits/forward_list.h @@ -60,7 +60,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _Fwd_list_node_base(const _Fwd_list_node_base&) = delete; _Fwd_list_node_base& operator=(const _Fwd_list_node_base&) = delete; -_Fwd_list_node_base& operator=(_Fwd_list_node_base&&) = delete; + +_Fwd_list_node_base& +operator=(_Fwd_list_node_base&& __x) +{ + _M_next = __x._M_next; + __x._M_next = nullptr; + return *this; +} _Fwd_list_node_base* _M_next = nullptr; @@ -75,7 +82,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER __end->_M_next = _M_next; } else - __begin->_M_next = 0; + __begin->_M_next = nullptr; _M_next = __keep; return __end; } @@ -180,7 +187,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER if (_M_node) return _Fwd_list_iterator(_M_node->_M_next); else - return _Fwd_list_iterator(0); + return _Fwd_list_iterator(nullptr); } _Fwd_list_node_base* _M_node; @@ -251,7 +258,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER if (this->_M_node) return _Fwd_list_const_iterator(_M_node->_M_next); else - return _Fwd_list_const_iterator(0); + return _Fwd_list_const_iterator(nullptr); } const _Fwd_list_node_base* _M_node; @@ -298,6 +305,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _Fwd_list_impl(_Fwd_list_impl&&) = default; + _Fwd_list_impl(_Fwd_list_impl&& __fl, _Node_alloc_type&& __a) + : _Node_alloc_type(std::move(__a)), _M_head(std::move(__fl._M_head)) + { } + _Fwd_list_impl(_Node_alloc_type&& __a) : _Node_alloc_type(std::move(__a)) { } @@ -323,15 +334,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _Fwd_list_base(_Node_alloc_type&& __a) : _M_impl(std::move(__a)) { } + // When allocators are always equal. + _Fwd_list_base(_Fwd_list_base&& __lst, _Node_alloc_type&& __a, + std::true_type) + : _M_impl(std::move(__lst._M_impl), std::move(__a)) + { } + + // When allocators are not always equal. _Fwd_list_base(_Fwd_list_base&& __lst, _Node_alloc_type&& __a); _Fwd_list_base(_Fwd_list_base&&) = default; ~_Fwd_list_base() - { _M_erase_after(&_M_impl._M_head, 0); } + { _M_erase_after(&_M_impl._M_head, nullptr); } protected: - _Node* _M_get_node() { @@ -448,7 +465,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER : _Base(_Node_alloc_type(__al)) { } - /** * @brief Copy constructor with allocator argument. * @param __list Input list to copy. @@ -458,14 +474,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER : _Base(_Node_alloc_type(__al)) { _M_range_initialize(__list.begin(), __list.end()); } - /** - * @brief Move constructor with allocator argument. - * @param __list Input list to move. - * @param __alAn allocator object. - */ - forward_list(forward_list&& __list, const _Alloc& __al) - noexcept(_Node_alloc_traits::_S_always_equal()) - : _Base(std::move(__list), _Node_alloc_type(__al)) +private: + forward_list(forward_list&& __list, _Node_alloc_type&& __al, + std::false_type) + : _Base(std::move(__list), std::move(__al)) { // If __list is not empty it means its allocator is not equal to __a, // so we need to move from each element individually. @@ -474,6 +486,24 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER std::__make_move_if_noexcept_iterator(__list.end())); } + forward_list(forward_list&& __list, _Node_alloc_type&& __al, + std::true_type) + noexcept + : _Base(std::move(__list), _Node_alloc_type(__al), std::true_type{}) + { } + +public: + /** + * @brief Move constructor with allocator argument. + * @param