* Default associative containers constructors/destructor/assignment @ 2016-10-28 19:43 François Dumont [not found] ` <24dd5816-4092-00f4-49b8-189d5ea68b52@gmail.com> 2016-11-17 17:52 ` Jonathan Wakely 0 siblings, 2 replies; 7+ messages in thread From: François Dumont @ 2016-10-28 19:43 UTC (permalink / raw) To: libstdc++, gcc-patches [-- Attachment #1: Type: text/plain, Size: 2267 bytes --] Hi Here is the patch to default all other associative containers operations that can be defaulted. To do so I introduce a _Rb_tree_key_compare type that take care of value initialization of compare functor. It also make sure that functor is copied rather than move in move constructor with necessary noexcept qualification. I also introduce _Rb_tree_header to take care of the initialization of the _Rb_tree_node_base used in the container header and of _M_node_count. I also use it to implement the move semantic and so default also _Rb_tree_impl move construtor. I also propose a solution for the FIXME regarding documentation of container destructor, I used C++11 default declaration. I don't have necessary tools to generate Doxygen doc but I am confident that it should work fine. I had to simplify doc for operations that are now defaulted. * include/bits/stl_map.h (map(const map&)): Make default. (map(map&&)): Likewise. (~map()): Likewise. (operator=(const map&)): Likewise. * include/bits/stl_multimap.h (multimap(const multimap&)): Make default. (multimap(multimap&&)): Likewise. (~multimap()): Likewise. (operator=(const multimap&)): Likewise. * include/bits/stl_set.h (set(const set&)): Make default. (set(set&&)): Likewise. (~set()): Likewise. (operator=(const set&)): Likewise. * include/bits/stl_multiset.h (multiset(const multiset&)): Make default. (multiset(multiset&&)): Likewise. (~multiset()): Likewise. (operator=(const multiset&)): Likewise. * include/bits/stl_tree.h (_Rb_tree_key_compare<>): New. (_Rb_tree_header): New. (_Rb_tree_impl): Inherit from latter. (_Rb_tree_impl()): Make default. (_Rb_tree_impl(const _Rb_tree_impl&)): New. (_Rb_tree_impl(_Rb_tree_impl&&)): New, default. (_Rb_tree_impl::_M_reset): Move... (_Rb_tree_header::_M_reset): ...here. (_Rb_tree_impl::_M_initialize): Move... (_Rb_tree_header::_M_initialize): ...here. (_Rb_tree(_Rb_tree&&)): Make default. (_Rb_tree_header::_M_move_data(_Rb_tree_header&)): New. (_Rb_tree<>::_M_move_data(_Rb_tree&, true_type)): Use latter. Tested under Linux x86_64, ok to commit ? François [-- Attachment #2: default_tree.patch --] [-- Type: text/x-patch, Size: 16559 bytes --] diff --git a/libstdc++-v3/include/bits/stl_map.h b/libstdc++-v3/include/bits/stl_map.h index dea7d5b..bbd0a97 100644 --- a/libstdc++-v3/include/bits/stl_map.h +++ b/libstdc++-v3/include/bits/stl_map.h @@ -185,25 +185,22 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER /** * @brief %Map copy constructor. - * @param __x A %map of identical element and allocator types. * - * The newly-created %map uses a copy of the allocator object used - * by @a __x (unless the allocator traits dictate a different object). + * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L map(const map& __x) : _M_t(__x._M_t) { } +#else + map(const map&) = default; -#if __cplusplus >= 201103L /** * @brief %Map move constructor. - * @param __x A %map of identical element and allocator types. * - * The newly-created %map contains the exact contents of @a __x. - * The contents of @a __x are a valid, but unspecified %map. + * The newly-created %map contains the exact contents of the moved + * instance. The moved instance is a valid, but unspecified, %map. */ - map(map&& __x) - noexcept(is_nothrow_copy_constructible<_Compare>::value) - : _M_t(std::move(__x._M_t)) { } + map(map&&) = default; /** * @brief Builds a %map from an initializer_list. @@ -284,31 +281,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER : _M_t(__comp, _Pair_alloc_type(__a)) { _M_t._M_insert_unique(__first, __last); } - // FIXME There is no dtor declared, but we should have something - // generated by Doxygen. I don't know what tags to add to this - // paragraph to make that happen: +#if __cplusplus >= 201103L /** * The dtor only erases the elements, and note that if the elements * themselves are pointers, the pointed-to memory is not touched in any * way. Managing the pointer is the user's responsibility. */ + ~map() = default; +#endif /** * @brief %Map assignment operator. - * @param __x A %map of identical element and allocator types. - * - * All the elements of @a __x are copied. * * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L map& operator=(const map& __x) { _M_t = __x._M_t; return *this; } +#else + map& + operator=(const map&) = default; -#if __cplusplus >= 201103L /// Move assignment operator. map& operator=(map&&) = default; diff --git a/libstdc++-v3/include/bits/stl_multimap.h b/libstdc++-v3/include/bits/stl_multimap.h index 7e86b76..a5f775b 100644 --- a/libstdc++-v3/include/bits/stl_multimap.h +++ b/libstdc++-v3/include/bits/stl_multimap.h @@ -182,25 +182,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER /** * @brief %Multimap copy constructor. - * @param __x A %multimap of identical element and allocator types. * - * The newly-created %multimap uses a copy of the allocator object used - * by @a __x (unless the allocator traits dictate a different object). + * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L multimap(const multimap& __x) : _M_t(__x._M_t) { } +#else + multimap(const multimap&) = default; -#if __cplusplus >= 201103L /** * @brief %Multimap move constructor. - * @param __x A %multimap of identical element and allocator types. * - * The newly-created %multimap contains the exact contents of @a __x. - * The contents of @a __x are a valid, but unspecified %multimap. + * The newly-created %multimap contains the exact contents of the + * moved instance. The moved instance is a valid, but unspecified + * %multimap. */ - multimap(multimap&& __x) - noexcept(is_nothrow_copy_constructible<_Compare>::value) - : _M_t(std::move(__x._M_t)) { } + multimap(multimap&&) = default; /** * @brief Builds a %multimap from an initializer_list. @@ -278,31 +276,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER : _M_t(__comp, _Pair_alloc_type(__a)) { _M_t._M_insert_equal(__first, __last); } - // FIXME There is no dtor declared, but we should have something generated - // by Doxygen. I don't know what tags to add to this paragraph to make - // that happen: +#if __cplusplus >= 201103L /** * The dtor only erases the elements, and note that if the elements * themselves are pointers, the pointed-to memory is not touched in any * way. Managing the pointer is the user's responsibility. */ + ~multimap() = default; +#endif /** * @brief %Multimap assignment operator. - * @param __x A %multimap of identical element and allocator types. - * - * All the elements of @a __x are copied. * * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L multimap& operator=(const multimap& __x) { _M_t = __x._M_t; return *this; } +#else + multimap& + operator=(const multimap&) = default; -#if __cplusplus >= 201103L /// Move assignment operator. multimap& operator=(multimap&&) = default; diff --git a/libstdc++-v3/include/bits/stl_multiset.h b/libstdc++-v3/include/bits/stl_multiset.h index 7fe2fbd..8a83b17 100644 --- a/libstdc++-v3/include/bits/stl_multiset.h +++ b/libstdc++-v3/include/bits/stl_multiset.h @@ -194,25 +194,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER /** * @brief %Multiset copy constructor. - * @param __x A %multiset of identical element and allocator types. * - * The newly-created %multiset uses a copy of the allocator object used - * by @a __x (unless the allocator traits dictate a different object). + * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L multiset(const multiset& __x) : _M_t(__x._M_t) { } +#else + multiset(const multiset&) = default; -#if __cplusplus >= 201103L /** * @brief %Multiset move constructor. - * @param __x A %multiset of identical element and allocator types. * - * The newly-created %multiset contains the exact contents of @a __x. - * The contents of @a __x are a valid, but unspecified %multiset. + * The newly-created %multiset contains the exact contents of the + * moved instance. The moved instance is a valid, but unspecified + * %multiset. */ - multiset(multiset&& __x) - noexcept(is_nothrow_copy_constructible<_Compare>::value) - : _M_t(std::move(__x._M_t)) { } + multiset(multiset&&) = default; /** * @brief Builds a %multiset from an initializer_list. @@ -256,24 +254,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER const allocator_type& __a) : _M_t(_Compare(), _Key_alloc_type(__a)) { _M_t._M_insert_equal(__first, __last); } + + /** + * The dtor only erases the elements, and note that if the elements + * themselves are pointers, the pointed-to memory is not touched in any + * way. Managing the pointer is the user's responsibility. + */ + ~multiset() = default; #endif /** * @brief %Multiset assignment operator. - * @param __x A %multiset of identical element and allocator types. - * - * All the elements of @a __x are copied. * * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L multiset& operator=(const multiset& __x) { _M_t = __x._M_t; return *this; } +#else + multiset& + operator=(const multiset&) = default; -#if __cplusplus >= 201103L /// Move assignment operator. multiset& operator=(multiset&&) = default; diff --git a/libstdc++-v3/include/bits/stl_set.h b/libstdc++-v3/include/bits/stl_set.h index 5ed9672..db1e031 100644 --- a/libstdc++-v3/include/bits/stl_set.h +++ b/libstdc++-v3/include/bits/stl_set.h @@ -199,25 +199,22 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER /** * @brief %Set copy constructor. - * @param __x A %set of identical element and allocator types. * - * The newly-created %set uses a copy of the allocator object used - * by @a __x (unless the allocator traits dictate a different object). + * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L set(const set& __x) : _M_t(__x._M_t) { } +#else + set(const set&) = default; -#if __cplusplus >= 201103L /** * @brief %Set move constructor - * @param __x A %set of identical element and allocator types. * - * The newly-created %set contains the exact contents of @a x. - * The contents of @a x are a valid, but unspecified %set. + * The newly-created %set contains the exact contents of the moved + * instance. The moved instance is a valid, but unspecified, %set. */ - set(set&& __x) - noexcept(is_nothrow_copy_constructible<_Compare>::value) - : _M_t(std::move(__x._M_t)) { } + set(set&&) = default; /** * @brief Builds a %set from an initializer_list. @@ -261,24 +258,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER const allocator_type& __a) : _M_t(_Compare(), _Key_alloc_type(__a)) { _M_t._M_insert_unique(__first, __last); } + + /** + * The dtor only erases the elements, and note that if the elements + * themselves are pointers, the pointed-to memory is not touched in any + * way. Managing the pointer is the user's responsibility. + */ + ~set() = default; #endif /** * @brief %Set assignment operator. - * @param __x A %set of identical element and allocator types. - * - * All the elements of @a __x are copied. * * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L set& operator=(const set& __x) { _M_t = __x._M_t; return *this; } +#else + set& + operator=(const set&) = default; -#if __cplusplus >= 201103L /// Move assignment operator. set& operator=(set&&) = default; diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h index 0bc5f4b..126087e 100644 --- a/libstdc++-v3/include/bits/stl_tree.h +++ b/libstdc++-v3/include/bits/stl_tree.h @@ -137,6 +137,81 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + // Helper type offering value initialization guaranty on the compare functor. + template<typename _Key_compare> + struct _Rb_tree_key_compare + { + _Key_compare _M_key_compare; + + _Rb_tree_key_compare() + _GLIBCXX_NOEXCEPT_IF( + is_nothrow_default_constructible<_Key_compare>::value) + : _M_key_compare() + { } + + _Rb_tree_key_compare(const _Key_compare& __comp) + : _M_key_compare(__comp) + { } + +#if __cplusplus >= 201103L + _Rb_tree_key_compare(_Rb_tree_key_compare&& __x) + noexcept(is_nothrow_copy_constructible<_Key_compare>::value) + : _M_key_compare(__x._M_key_compare) + { } +#endif + }; + + // Helper type to manage default initialization of node count and header. + struct _Rb_tree_header + { + _Rb_tree_node_base _M_header; + size_t _M_node_count; // Keeps track of size of tree. + + _Rb_tree_header() _GLIBCXX_NOEXCEPT + : _M_node_count(0) + { _M_initialize(); } + +#if __cplusplus >= 201103L + _Rb_tree_header(_Rb_tree_header&& __x) noexcept + : _M_node_count(0) + { + if (__x._M_header._M_parent != nullptr) + _M_move_data(__x); + else + _M_initialize(); + } + + void + _M_move_data(_Rb_tree_header& __x) + { + _M_header._M_parent = __x._M_header._M_parent; + _M_header._M_left = __x._M_header._M_left; + _M_header._M_right = __x._M_header._M_right; + _M_header._M_parent->_M_parent = &_M_header; + _M_node_count = __x._M_node_count; + + __x._M_reset(); + } +#endif + + void + _M_reset() + { + _M_initialize(); + _M_node_count = 0; + } + + private: + void + _M_initialize() + { + _M_header._M_color = _S_red; + _M_header._M_parent = 0; + _M_header._M_left = &_M_header; + _M_header._M_right = &_M_header; + } + }; + template<typename _Val> struct _Rb_tree_node : public _Rb_tree_node_base { @@ -599,50 +674,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Unused _Is_pod_comparator is kept as it is part of mangled name. template<typename _Key_compare, bool /* _Is_pod_comparator */ = __is_pod(_Key_compare)> - struct _Rb_tree_impl : public _Node_allocator + struct _Rb_tree_impl + : public _Node_allocator + , public _Rb_tree_key_compare<_Key_compare> + , public _Rb_tree_header { - _Key_compare _M_key_compare; - _Rb_tree_node_base _M_header; - size_type _M_node_count; // Keeps track of size of tree. + typedef _Rb_tree_key_compare<_Key_compare> _Base_key_compare; +#if __cplusplus < 201103L _Rb_tree_impl() - _GLIBCXX_NOEXCEPT_IF( - is_nothrow_default_constructible<_Node_allocator>::value - && is_nothrow_default_constructible<_Key_compare>::value) - : _Node_allocator(), _M_key_compare(), _M_header(), - _M_node_count(0) - { _M_initialize(); } + { } +#else + _Rb_tree_impl() = default; +#endif - _Rb_tree_impl(const _Key_compare& __comp, const _Node_allocator& __a) - : _Node_allocator(__a), _M_key_compare(__comp), _M_header(), - _M_node_count(0) - { _M_initialize(); } + _Rb_tree_impl(const _Rb_tree_impl& __x) + : _Node_allocator(_Alloc_traits::_S_select_on_copy(__x)) + , _Base_key_compare(__x._M_key_compare) + { } #if __cplusplus >= 201103L + _Rb_tree_impl(_Rb_tree_impl&&) = default; _Rb_tree_impl(const _Key_compare& __comp, _Node_allocator&& __a) - : _Node_allocator(std::move(__a)), _M_key_compare(__comp), - _M_header(), _M_node_count(0) - { _M_initialize(); } + : _Node_allocator(std::move(__a)), _Base_key_compare(__comp) + { } #endif - - void - _M_reset() - { - this->_M_header._M_parent = 0; - this->_M_header._M_left = &this->_M_header; - this->_M_header._M_right = &this->_M_header; - this->_M_node_count = 0; - } - - private: - void - _M_initialize() - { - this->_M_header._M_color = _S_red; - this->_M_header._M_parent = 0; - this->_M_header._M_left = &this->_M_header; - this->_M_header._M_right = &this->_M_header; - } }; _Rb_tree_impl<_Compare> _M_impl; @@ -845,8 +901,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _M_impl(__comp, _Node_allocator(__a)) { } _Rb_tree(const _Rb_tree& __x) - : _M_impl(__x._M_impl._M_key_compare, - _Alloc_traits::_S_select_on_copy(__x._M_get_Node_allocator())) + : _M_impl(__x._M_impl) { if (__x._M_root() != 0) { @@ -874,13 +929,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } - _Rb_tree(_Rb_tree&& __x) - : _M_impl(__x._M_impl._M_key_compare, - std::move(__x._M_get_Node_allocator())) - { - if (__x._M_root() != 0) - _M_move_data(__x, std::true_type()); - } + _Rb_tree(_Rb_tree&&) = default; _Rb_tree(_Rb_tree&& __x, const allocator_type& __a) : _Rb_tree(std::move(__x), _Node_allocator(__a)) @@ -1534,19 +1583,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>:: _M_move_data(_Rb_tree& __x, std::true_type) - { - _M_root() = __x._M_root(); - _M_leftmost() = __x._M_leftmost(); - _M_rightmost() = __x._M_rightmost(); - _M_root()->_M_parent = _M_end(); - - __x._M_root() = 0; - __x._M_leftmost() = __x._M_end(); - __x._M_rightmost() = __x._M_end(); - - this->_M_impl._M_node_count = __x._M_impl._M_node_count; - __x._M_impl._M_node_count = 0; - } + { _M_impl._M_move_data(__x._M_impl); } template<typename _Key, typename _Val, typename _KeyOfValue, typename _Compare, typename _Alloc> ^ permalink raw reply [flat|nested] 7+ messages in thread
[parent not found: <24dd5816-4092-00f4-49b8-189d5ea68b52@gmail.com>]
* Re: Default associative containers constructors/destructor/assignment [not found] ` <24dd5816-4092-00f4-49b8-189d5ea68b52@gmail.com> @ 2016-11-14 20:35 ` François Dumont 0 siblings, 0 replies; 7+ messages in thread From: François Dumont @ 2016-11-14 20:35 UTC (permalink / raw) To: libstdc++, gcc-patches Any feedback regarding this patch ? François On 02/11/2016 22:37, François Dumont wrote: > Hi > > Here is an updated proposal, I realized that the newly introduced > _M_move_data can be also used in the swap implementation. > > Let me know if you prefer without it or not. > > François > > > On 28/10/2016 21:42, François Dumont wrote: >> Hi >> >> Here is the patch to default all other associative containers >> operations that can be defaulted. >> >> To do so I introduce a _Rb_tree_key_compare type that take care >> of value initialization of compare functor. It also make sure that >> functor is copied rather than move in move constructor with necessary >> noexcept qualification. >> >> I also introduce _Rb_tree_header to take care of the >> initialization of the _Rb_tree_node_base used in the container header >> and of _M_node_count. I also use it to implement the move semantic >> and so default also _Rb_tree_impl move construtor. >> >> I also propose a solution for the FIXME regarding documentation >> of container destructor, I used C++11 default declaration. I don't >> have necessary tools to generate Doxygen doc but I am confident that >> it should work fine. I had to simplify doc for operations that are >> now defaulted. >> >> >> * include/bits/stl_map.h (map(const map&)): Make default. >> (map(map&&)): Likewise. >> (~map()): Likewise. >> (operator=(const map&)): Likewise. >> * include/bits/stl_multimap.h (multimap(const multimap&)): Make >> default. >> (multimap(multimap&&)): Likewise. >> (~multimap()): Likewise. >> (operator=(const multimap&)): Likewise. >> * include/bits/stl_set.h (set(const set&)): Make default. >> (set(set&&)): Likewise. >> (~set()): Likewise. >> (operator=(const set&)): Likewise. >> * include/bits/stl_multiset.h (multiset(const multiset&)): Make >> default. >> (multiset(multiset&&)): Likewise. >> (~multiset()): Likewise. >> (operator=(const multiset&)): Likewise. >> * include/bits/stl_tree.h (_Rb_tree_key_compare<>): New. >> (_Rb_tree_header): New. >> (_Rb_tree_impl): Inherit from latter. >> (_Rb_tree_impl()): Make default. >> (_Rb_tree_impl(const _Rb_tree_impl&)): New. >> (_Rb_tree_impl(_Rb_tree_impl&&)): New, default. >> (_Rb_tree_impl::_M_reset): Move... >> (_Rb_tree_header::_M_reset): ...here. >> (_Rb_tree_impl::_M_initialize): Move... >> (_Rb_tree_header::_M_initialize): ...here. >> (_Rb_tree(_Rb_tree&&)): Make default. >> (_Rb_tree_header::_M_move_data(_Rb_tree_header&)): New. >> (_Rb_tree<>::_M_move_data(_Rb_tree&, true_type)): Use latter. >> >> Tested under Linux x86_64, ok to commit ? >> >> François >> > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Default associative containers constructors/destructor/assignment 2016-10-28 19:43 Default associative containers constructors/destructor/assignment François Dumont [not found] ` <24dd5816-4092-00f4-49b8-189d5ea68b52@gmail.com> @ 2016-11-17 17:52 ` Jonathan Wakely 2016-11-20 18:14 ` François Dumont 1 sibling, 1 reply; 7+ messages in thread From: Jonathan Wakely @ 2016-11-17 17:52 UTC (permalink / raw) To: François Dumont; +Cc: libstdc++, gcc-patches On 28/10/16 21:42 +0200, François Dumont wrote: > /** > * @brief %Map move constructor. >- * @param __x A %map of identical element and allocator types. > * >- * The newly-created %map contains the exact contents of @a __x. >- * The contents of @a __x are a valid, but unspecified %map. >+ * The newly-created %map contains the exact contents of the moved >+ * instance. The moved instance is a valid, but unspecified, %map. This comment isn't true, because non-propagating or non-equal allocators can force elements to be copied, but that's unrelated to your patch. There are no problems with the changes to the map and set containers, but I have some comments on the _Rb_tree changes. Overall I like the change. >diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h >index 0bc5f4b..126087e 100644 >--- a/libstdc++-v3/include/bits/stl_tree.h >+++ b/libstdc++-v3/include/bits/stl_tree.h >@@ -137,6 +137,81 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > } > }; > >+ // Helper type offering value initialization guaranty on the compare functor. Spelling: "guarantee" >+ template<typename _Key_compare> >+ struct _Rb_tree_key_compare >+ { >+ _Key_compare _M_key_compare; >+ >+ _Rb_tree_key_compare() >+ _GLIBCXX_NOEXCEPT_IF( >+ is_nothrow_default_constructible<_Key_compare>::value) >+ : _M_key_compare() >+ { } >+ >+ _Rb_tree_key_compare(const _Key_compare& __comp) >+ : _M_key_compare(__comp) >+ { } >+ >+#if __cplusplus >= 201103L >+ _Rb_tree_key_compare(_Rb_tree_key_compare&& __x) >+ noexcept(is_nothrow_copy_constructible<_Key_compare>::value) >+ : _M_key_compare(__x._M_key_compare) >+ { } >+#endif This constructor makes the type move-only (i.e. non-copyable) in C++11 and later. It's copyable in C++98. Is that what you want? Adding defaulted copy operations would fix that. Even if we don't actually need those copy operations, I'm uncomfortable with it being copyable in C++98 and non-copyable otherwise. >+ void >+ _M_reset() >+ { >+ _M_initialize(); >+ _M_node_count = 0; >+ } This introduces a small change in behaviour, because _M_reset() now does _M_header._M_color = _S_red, which it didn't do before. That store is redundant. It could be avoided by just doing the three assignments in _M_reset() instead of calling _M_initialize(). And we could remove _M_initialize() entirely, and remove the redundant mem-initializers for _M_node_count (because it's set my _M_reset and _M_move_data anyway): _Rb_tree_header() _GLIBCXX_NOEXCEPT { _M_reset(); _M_header._M_color = _S_red; } #if __cplusplus >= 201103L _Rb_tree_header(_Rb_tree_header&& __x) noexcept { if (__x._M_header._M_parent != nullptr) _M_move_data(__x); else { _M_reset(); _M_header._M_color = _S_red; } } void _M_move_data(_Rb_tree_header& __x) { _M_header._M_parent = __x._M_header._M_parent; _M_header._M_left = __x._M_header._M_left; _M_header._M_right = __x._M_header._M_right; _M_header._M_parent->_M_parent = &_M_header; _M_node_count = __x._M_node_count; __x._M_reset(); } #endif void _M_reset() { _M_header._M_parent = 0; _M_header._M_left = &_M_header; _M_header._M_right = &_M_header; _M_node_count = 0; } >@@ -599,50 +674,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > // Unused _Is_pod_comparator is kept as it is part of mangled name. > template<typename _Key_compare, > bool /* _Is_pod_comparator */ = __is_pod(_Key_compare)> >- struct _Rb_tree_impl : public _Node_allocator >+ struct _Rb_tree_impl >+ : public _Node_allocator >+ , public _Rb_tree_key_compare<_Key_compare> >+ , public _Rb_tree_header Do these need to be base classes, rather than data members? We derive from the allocator to benefit from the empty base-class optimization, but that isn't relevant for the _Rb_tree_key_compare and _Rb_tree_header types. It *could* be relevant for the comparison function, but would be an ABI change. We could do that ABI change conditionally, for gnu-versioned-namespace builds, but that's still possible using data members (e.g. have a data member that derives from the comparison function and contains the header node and/or node count members). Making them data members would simply mean restoring the function _Rb_tree_impl::_M_reset() and making it call reset on the member: void _M_reset() { _M_header._M_reset(); } The minor convenience of inheriting this function from a base class doesn't seem worth the stronger coupling that comes from using inheritance. Am I missing some other reason that inheritance is used here? >- _Rb_tree_impl(const _Key_compare& __comp, const _Node_allocator& __a) >- : _Node_allocator(__a), _M_key_compare(__comp), _M_header(), >- _M_node_count(0) >- { _M_initialize(); } Please mention the removal of this constructor in the changelog. >@@ -1534,19 +1583,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > void > _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>:: > _M_move_data(_Rb_tree& __x, std::true_type) >- { >- _M_root() = __x._M_root(); >- _M_leftmost() = __x._M_leftmost(); >- _M_rightmost() = __x._M_rightmost(); >- _M_root()->_M_parent = _M_end(); >- >- __x._M_root() = 0; >- __x._M_leftmost() = __x._M_end(); >- __x._M_rightmost() = __x._M_end(); >- >- this->_M_impl._M_node_count = __x._M_impl._M_node_count; >- __x._M_impl._M_node_count = 0; >- } >+ { _M_impl._M_move_data(__x._M_impl); } This function could be moved into the class body, or just have 'inline' added to its definition. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Default associative containers constructors/destructor/assignment 2016-11-17 17:52 ` Jonathan Wakely @ 2016-11-20 18:14 ` François Dumont 2016-12-06 11:54 ` Jonathan Wakely 0 siblings, 1 reply; 7+ messages in thread From: François Dumont @ 2016-11-20 18:14 UTC (permalink / raw) To: Jonathan Wakely; +Cc: libstdc++, gcc-patches [-- Attachment #1: Type: text/plain, Size: 6412 bytes --] On 17/11/2016 18:52, Jonathan Wakely wrote: > > On 28/10/16 21:42 +0200, François Dumont wrote: > >> + template<typename _Key_compare> >> + struct _Rb_tree_key_compare >> + { >> + _Key_compare _M_key_compare; >> + >> + _Rb_tree_key_compare() >> + _GLIBCXX_NOEXCEPT_IF( >> + is_nothrow_default_constructible<_Key_compare>::value) >> + : _M_key_compare() >> + { } >> + >> + _Rb_tree_key_compare(const _Key_compare& __comp) >> + : _M_key_compare(__comp) >> + { } >> + >> +#if __cplusplus >= 201103L >> + _Rb_tree_key_compare(_Rb_tree_key_compare&& __x) >> + noexcept(is_nothrow_copy_constructible<_Key_compare>::value) >> + : _M_key_compare(__x._M_key_compare) >> + { } >> +#endif > > This constructor makes the type move-only (i.e. non-copyable) in > C++11 and later. It's copyable in C++98. Is that what you want? I simply consider it was not a problem as, as you noticed, it is not used. > > Adding defaulted copy operations would fix that. Even if we don't > actually need those copy operations, I'm uncomfortable with it being > copyable in C++98 and non-copyable otherwise. Ok, I'll add a default copy constructor for consistency like this: // Copy constructor added for consistency with C++98 mode. _Rb_tree_key_compare(const _Rb_tree_compare&) = default; > >> + void >> + _M_reset() >> + { >> + _M_initialize(); >> + _M_node_count = 0; >> + } > > This introduces a small change in behaviour, because _M_reset() now > does _M_header._M_color = _S_red, which it didn't do before. That > store is redundant. It could be avoided by just doing the three > assignments in _M_reset() instead of calling _M_initialize(). I thought it was ok to do this additional operation for the sake of reusing _M_initialize(). I was compenciating it by avoiding default initialization when there is something to move. > > And we could remove _M_initialize() entirely, and remove the redundant > mem-initializers for _M_node_count (because it's set my _M_reset and > _M_move_data anyway): > > _Rb_tree_header() _GLIBCXX_NOEXCEPT > { > _M_reset(); > _M_header._M_color = _S_red; > } > > #if __cplusplus >= 201103L > _Rb_tree_header(_Rb_tree_header&& __x) noexcept > { > if (__x._M_header._M_parent != nullptr) > _M_move_data(__x); > else > { > _M_reset(); > _M_header._M_color = _S_red; > } > } > > void > _M_move_data(_Rb_tree_header& __x) > { > _M_header._M_parent = __x._M_header._M_parent; > _M_header._M_left = __x._M_header._M_left; > _M_header._M_right = __x._M_header._M_right; > _M_header._M_parent->_M_parent = &_M_header; > _M_node_count = __x._M_node_count; > > __x._M_reset(); > } > #endif > > void > _M_reset() > { > _M_header._M_parent = 0; > _M_header._M_left = &_M_header; > _M_header._M_right = &_M_header; > _M_node_count = 0; > } > Yes, looks nice, adopted. Talking about _M_color I just realize that move semantic doesn't copy _M_color. Shouldn't we capture it along with all the other _M_header information ? > >> @@ -599,50 +674,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> // Unused _Is_pod_comparator is kept as it is part of mangled >> name. >> template<typename _Key_compare, >> bool /* _Is_pod_comparator */ = __is_pod(_Key_compare)> >> - struct _Rb_tree_impl : public _Node_allocator >> + struct _Rb_tree_impl >> + : public _Node_allocator >> + , public _Rb_tree_key_compare<_Key_compare> >> + , public _Rb_tree_header > > Do these need to be base classes, rather than data members? > > We derive from the allocator to benefit from the empty base-class > optimization, but that isn't relevant for the _Rb_tree_key_compare and > _Rb_tree_header types. It *could* be relevant for the comparison > function, but would be an ABI change. We could do that ABI change > conditionally, for gnu-versioned-namespace builds, but that's still > possible using data members (e.g. have a data member that derives from > the comparison function and contains the header node and/or node count > members). > > Making them data members would simply mean restoring the function > _Rb_tree_impl::_M_reset() and making it call reset on the member: > > void > _M_reset() { _M_header._M_reset(); } > > The minor convenience of inheriting this function from a base class > doesn't seem worth the stronger coupling that comes from using > inheritance. > > Am I missing some other reason that inheritance is used here? The purpose of this patch is to rely more on compiler especially in regards to the noexcept qualifications. This is why I started isolating each ressource in its own class in charge of its specificities. And I leave to the compiler the work of combining those types. However I also wanted to limit the impact of this patch on the _Rb_tree and still be able to use things like this->_M_impl._M_node_count or this->_M_impl._M_header. So the usage of inheritance. > >> - _Rb_tree_impl(const _Key_compare& __comp, const >> _Node_allocator& __a) >> - : _Node_allocator(__a), _M_key_compare(__comp), _M_header(), >> - _M_node_count(0) >> - { _M_initialize(); } > > Please mention the removal of this constructor in the changelog. > >> @@ -1534,19 +1583,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> void >> _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>:: >> _M_move_data(_Rb_tree& __x, std::true_type) >> - { >> - _M_root() = __x._M_root(); >> - _M_leftmost() = __x._M_leftmost(); >> - _M_rightmost() = __x._M_rightmost(); >> - _M_root()->_M_parent = _M_end(); >> - >> - __x._M_root() = 0; >> - __x._M_leftmost() = __x._M_end(); >> - __x._M_rightmost() = __x._M_end(); >> - >> - this->_M_impl._M_node_count = __x._M_impl._M_node_count; >> - __x._M_impl._M_node_count = 0; >> - } >> + { _M_impl._M_move_data(__x._M_impl); } > > This function could be moved into the class body, or just have > 'inline' added to its definition. > Ok, adopted in the attached new version of the patch. Tested under x86_64 Linux, ok to commit ? For info, I would like to propose another bunch of simplifications of _Rb_tree, see other patch attached. Do you want me to propose it in another mail ? François [-- Attachment #2: default_tree.patch --] [-- Type: text/x-patch, Size: 18055 bytes --] diff --git a/libstdc++-v3/include/bits/stl_map.h b/libstdc++-v3/include/bits/stl_map.h index dea7d5b..bbd0a97 100644 --- a/libstdc++-v3/include/bits/stl_map.h +++ b/libstdc++-v3/include/bits/stl_map.h @@ -185,25 +185,22 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER /** * @brief %Map copy constructor. - * @param __x A %map of identical element and allocator types. * - * The newly-created %map uses a copy of the allocator object used - * by @a __x (unless the allocator traits dictate a different object). + * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L map(const map& __x) : _M_t(__x._M_t) { } +#else + map(const map&) = default; -#if __cplusplus >= 201103L /** * @brief %Map move constructor. - * @param __x A %map of identical element and allocator types. * - * The newly-created %map contains the exact contents of @a __x. - * The contents of @a __x are a valid, but unspecified %map. + * The newly-created %map contains the exact contents of the moved + * instance. The moved instance is a valid, but unspecified, %map. */ - map(map&& __x) - noexcept(is_nothrow_copy_constructible<_Compare>::value) - : _M_t(std::move(__x._M_t)) { } + map(map&&) = default; /** * @brief Builds a %map from an initializer_list. @@ -284,31 +281,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER : _M_t(__comp, _Pair_alloc_type(__a)) { _M_t._M_insert_unique(__first, __last); } - // FIXME There is no dtor declared, but we should have something - // generated by Doxygen. I don't know what tags to add to this - // paragraph to make that happen: +#if __cplusplus >= 201103L /** * The dtor only erases the elements, and note that if the elements * themselves are pointers, the pointed-to memory is not touched in any * way. Managing the pointer is the user's responsibility. */ + ~map() = default; +#endif /** * @brief %Map assignment operator. - * @param __x A %map of identical element and allocator types. - * - * All the elements of @a __x are copied. * * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L map& operator=(const map& __x) { _M_t = __x._M_t; return *this; } +#else + map& + operator=(const map&) = default; -#if __cplusplus >= 201103L /// Move assignment operator. map& operator=(map&&) = default; diff --git a/libstdc++-v3/include/bits/stl_multimap.h b/libstdc++-v3/include/bits/stl_multimap.h index 7e86b76..a5f775b 100644 --- a/libstdc++-v3/include/bits/stl_multimap.h +++ b/libstdc++-v3/include/bits/stl_multimap.h @@ -182,25 +182,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER /** * @brief %Multimap copy constructor. - * @param __x A %multimap of identical element and allocator types. * - * The newly-created %multimap uses a copy of the allocator object used - * by @a __x (unless the allocator traits dictate a different object). + * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L multimap(const multimap& __x) : _M_t(__x._M_t) { } +#else + multimap(const multimap&) = default; -#if __cplusplus >= 201103L /** * @brief %Multimap move constructor. - * @param __x A %multimap of identical element and allocator types. * - * The newly-created %multimap contains the exact contents of @a __x. - * The contents of @a __x are a valid, but unspecified %multimap. + * The newly-created %multimap contains the exact contents of the + * moved instance. The moved instance is a valid, but unspecified + * %multimap. */ - multimap(multimap&& __x) - noexcept(is_nothrow_copy_constructible<_Compare>::value) - : _M_t(std::move(__x._M_t)) { } + multimap(multimap&&) = default; /** * @brief Builds a %multimap from an initializer_list. @@ -278,31 +276,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER : _M_t(__comp, _Pair_alloc_type(__a)) { _M_t._M_insert_equal(__first, __last); } - // FIXME There is no dtor declared, but we should have something generated - // by Doxygen. I don't know what tags to add to this paragraph to make - // that happen: +#if __cplusplus >= 201103L /** * The dtor only erases the elements, and note that if the elements * themselves are pointers, the pointed-to memory is not touched in any * way. Managing the pointer is the user's responsibility. */ + ~multimap() = default; +#endif /** * @brief %Multimap assignment operator. - * @param __x A %multimap of identical element and allocator types. - * - * All the elements of @a __x are copied. * * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L multimap& operator=(const multimap& __x) { _M_t = __x._M_t; return *this; } +#else + multimap& + operator=(const multimap&) = default; -#if __cplusplus >= 201103L /// Move assignment operator. multimap& operator=(multimap&&) = default; diff --git a/libstdc++-v3/include/bits/stl_multiset.h b/libstdc++-v3/include/bits/stl_multiset.h index 7fe2fbd..8a83b17 100644 --- a/libstdc++-v3/include/bits/stl_multiset.h +++ b/libstdc++-v3/include/bits/stl_multiset.h @@ -194,25 +194,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER /** * @brief %Multiset copy constructor. - * @param __x A %multiset of identical element and allocator types. * - * The newly-created %multiset uses a copy of the allocator object used - * by @a __x (unless the allocator traits dictate a different object). + * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L multiset(const multiset& __x) : _M_t(__x._M_t) { } +#else + multiset(const multiset&) = default; -#if __cplusplus >= 201103L /** * @brief %Multiset move constructor. - * @param __x A %multiset of identical element and allocator types. * - * The newly-created %multiset contains the exact contents of @a __x. - * The contents of @a __x are a valid, but unspecified %multiset. + * The newly-created %multiset contains the exact contents of the + * moved instance. The moved instance is a valid, but unspecified + * %multiset. */ - multiset(multiset&& __x) - noexcept(is_nothrow_copy_constructible<_Compare>::value) - : _M_t(std::move(__x._M_t)) { } + multiset(multiset&&) = default; /** * @brief Builds a %multiset from an initializer_list. @@ -256,24 +254,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER const allocator_type& __a) : _M_t(_Compare(), _Key_alloc_type(__a)) { _M_t._M_insert_equal(__first, __last); } + + /** + * The dtor only erases the elements, and note that if the elements + * themselves are pointers, the pointed-to memory is not touched in any + * way. Managing the pointer is the user's responsibility. + */ + ~multiset() = default; #endif /** * @brief %Multiset assignment operator. - * @param __x A %multiset of identical element and allocator types. - * - * All the elements of @a __x are copied. * * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L multiset& operator=(const multiset& __x) { _M_t = __x._M_t; return *this; } +#else + multiset& + operator=(const multiset&) = default; -#if __cplusplus >= 201103L /// Move assignment operator. multiset& operator=(multiset&&) = default; diff --git a/libstdc++-v3/include/bits/stl_set.h b/libstdc++-v3/include/bits/stl_set.h index 5ed9672..db1e031 100644 --- a/libstdc++-v3/include/bits/stl_set.h +++ b/libstdc++-v3/include/bits/stl_set.h @@ -199,25 +199,22 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER /** * @brief %Set copy constructor. - * @param __x A %set of identical element and allocator types. * - * The newly-created %set uses a copy of the allocator object used - * by @a __x (unless the allocator traits dictate a different object). + * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L set(const set& __x) : _M_t(__x._M_t) { } +#else + set(const set&) = default; -#if __cplusplus >= 201103L /** * @brief %Set move constructor - * @param __x A %set of identical element and allocator types. * - * The newly-created %set contains the exact contents of @a x. - * The contents of @a x are a valid, but unspecified %set. + * The newly-created %set contains the exact contents of the moved + * instance. The moved instance is a valid, but unspecified, %set. */ - set(set&& __x) - noexcept(is_nothrow_copy_constructible<_Compare>::value) - : _M_t(std::move(__x._M_t)) { } + set(set&&) = default; /** * @brief Builds a %set from an initializer_list. @@ -261,24 +258,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER const allocator_type& __a) : _M_t(_Compare(), _Key_alloc_type(__a)) { _M_t._M_insert_unique(__first, __last); } + + /** + * The dtor only erases the elements, and note that if the elements + * themselves are pointers, the pointed-to memory is not touched in any + * way. Managing the pointer is the user's responsibility. + */ + ~set() = default; #endif /** * @brief %Set assignment operator. - * @param __x A %set of identical element and allocator types. - * - * All the elements of @a __x are copied. * * Whether the allocator is copied depends on the allocator traits. */ +#if __cplusplus < 201103L set& operator=(const set& __x) { _M_t = __x._M_t; return *this; } +#else + set& + operator=(const set&) = default; -#if __cplusplus >= 201103L /// Move assignment operator. set& operator=(set&&) = default; diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h index 0bc5f4b..8de5e31 100644 --- a/libstdc++-v3/include/bits/stl_tree.h +++ b/libstdc++-v3/include/bits/stl_tree.h @@ -137,6 +137,80 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + // Helper type offering value initialization guarantee on the compare functor. + template<typename _Key_compare> + struct _Rb_tree_key_compare + { + _Key_compare _M_key_compare; + + _Rb_tree_key_compare() + _GLIBCXX_NOEXCEPT_IF( + is_nothrow_default_constructible<_Key_compare>::value) + : _M_key_compare() + { } + + _Rb_tree_key_compare(const _Key_compare& __comp) + : _M_key_compare(__comp) + { } + +#if __cplusplus >= 201103L + // Copy constructor added for consistency with C++98 mode. + _Rb_tree_key_compare(const _Rb_tree_key_compare&) = default; + + _Rb_tree_key_compare(_Rb_tree_key_compare&& __x) + noexcept(is_nothrow_copy_constructible<_Key_compare>::value) + : _M_key_compare(__x._M_key_compare) + { } +#endif + }; + + // Helper type to manage default initialization of node count and header. + struct _Rb_tree_header + { + _Rb_tree_node_base _M_header; + size_t _M_node_count; // Keeps track of size of tree. + + _Rb_tree_header() _GLIBCXX_NOEXCEPT + { + _M_header._M_color = _S_red; + _M_reset(); + } + +#if __cplusplus >= 201103L + _Rb_tree_header(_Rb_tree_header&& __x) noexcept + { + if (__x._M_header._M_parent != nullptr) + _M_move_data(__x); + else + { + _M_header._M_color = _S_red; + _M_reset(); + } + } +#endif + + void + _M_move_data(_Rb_tree_header& __from) + { + _M_header._M_parent = __from._M_header._M_parent; + _M_header._M_left = __from._M_header._M_left; + _M_header._M_right = __from._M_header._M_right; + _M_header._M_parent->_M_parent = &_M_header; + _M_node_count = __from._M_node_count; + + __from._M_reset(); + } + + void + _M_reset() + { + _M_header._M_parent = 0; + _M_header._M_left = &_M_header; + _M_header._M_right = &_M_header; + _M_node_count = 0; + } + }; + template<typename _Val> struct _Rb_tree_node : public _Rb_tree_node_base { @@ -599,50 +673,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Unused _Is_pod_comparator is kept as it is part of mangled name. template<typename _Key_compare, bool /* _Is_pod_comparator */ = __is_pod(_Key_compare)> - struct _Rb_tree_impl : public _Node_allocator + struct _Rb_tree_impl + : public _Node_allocator + , public _Rb_tree_key_compare<_Key_compare> + , public _Rb_tree_header { - _Key_compare _M_key_compare; - _Rb_tree_node_base _M_header; - size_type _M_node_count; // Keeps track of size of tree. + typedef _Rb_tree_key_compare<_Key_compare> _Base_key_compare; +#if __cplusplus < 201103L _Rb_tree_impl() - _GLIBCXX_NOEXCEPT_IF( - is_nothrow_default_constructible<_Node_allocator>::value - && is_nothrow_default_constructible<_Key_compare>::value) - : _Node_allocator(), _M_key_compare(), _M_header(), - _M_node_count(0) - { _M_initialize(); } + { } +#else + _Rb_tree_impl() = default; +#endif - _Rb_tree_impl(const _Key_compare& __comp, const _Node_allocator& __a) - : _Node_allocator(__a), _M_key_compare(__comp), _M_header(), - _M_node_count(0) - { _M_initialize(); } + _Rb_tree_impl(const _Rb_tree_impl& __x) + : _Node_allocator(_Alloc_traits::_S_select_on_copy(__x)) + , _Base_key_compare(__x._M_key_compare) + { } #if __cplusplus >= 201103L + _Rb_tree_impl(_Rb_tree_impl&&) = default; _Rb_tree_impl(const _Key_compare& __comp, _Node_allocator&& __a) - : _Node_allocator(std::move(__a)), _M_key_compare(__comp), - _M_header(), _M_node_count(0) - { _M_initialize(); } + : _Node_allocator(std::move(__a)), _Base_key_compare(__comp) + { } #endif - - void - _M_reset() - { - this->_M_header._M_parent = 0; - this->_M_header._M_left = &this->_M_header; - this->_M_header._M_right = &this->_M_header; - this->_M_node_count = 0; - } - - private: - void - _M_initialize() - { - this->_M_header._M_color = _S_red; - this->_M_header._M_parent = 0; - this->_M_header._M_left = &this->_M_header; - this->_M_header._M_right = &this->_M_header; - } }; _Rb_tree_impl<_Compare> _M_impl; @@ -845,8 +900,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _M_impl(__comp, _Node_allocator(__a)) { } _Rb_tree(const _Rb_tree& __x) - : _M_impl(__x._M_impl._M_key_compare, - _Alloc_traits::_S_select_on_copy(__x._M_get_Node_allocator())) + : _M_impl(__x._M_impl) { if (__x._M_root() != 0) { @@ -874,13 +928,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } - _Rb_tree(_Rb_tree&& __x) - : _M_impl(__x._M_impl._M_key_compare, - std::move(__x._M_get_Node_allocator())) - { - if (__x._M_root() != 0) - _M_move_data(__x, std::true_type()); - } + _Rb_tree(_Rb_tree&&) = default; _Rb_tree(_Rb_tree&& __x, const allocator_type& __a) : _Rb_tree(std::move(__x), _Node_allocator(__a)) @@ -1278,7 +1326,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: // Move elements from container with equal allocator. void - _M_move_data(_Rb_tree&, std::true_type); + _M_move_data(_Rb_tree& __x, std::true_type) + { _M_impl._M_move_data(__x._M_impl); } // Move elements from container with possibly non-equal allocator, // which might result in a copy not a move. @@ -1533,25 +1582,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typename _Compare, typename _Alloc> void _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>:: - _M_move_data(_Rb_tree& __x, std::true_type) - { - _M_root() = __x._M_root(); - _M_leftmost() = __x._M_leftmost(); - _M_rightmost() = __x._M_rightmost(); - _M_root()->_M_parent = _M_end(); - - __x._M_root() = 0; - __x._M_leftmost() = __x._M_end(); - __x._M_rightmost() = __x._M_end(); - - this->_M_impl._M_node_count = __x._M_impl._M_node_count; - __x._M_impl._M_node_count = 0; - } - - template<typename _Key, typename _Val, typename _KeyOfValue, - typename _Compare, typename _Alloc> - void - _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>:: _M_move_data(_Rb_tree& __x, std::false_type) { if (_M_get_Node_allocator() == __x._M_get_Node_allocator()) @@ -1966,26 +1996,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (_M_root() == 0) { if (__t._M_root() != 0) - { - _M_root() = __t._M_root(); - _M_leftmost() = __t._M_leftmost(); - _M_rightmost() = __t._M_rightmost(); - _M_root()->_M_parent = _M_end(); - _M_impl._M_node_count = __t._M_impl._M_node_count; - - __t._M_impl._M_reset(); - } + _M_impl._M_move_data(__t._M_impl); } else if (__t._M_root() == 0) - { - __t._M_root() = _M_root(); - __t._M_leftmost() = _M_leftmost(); - __t._M_rightmost() = _M_rightmost(); - __t._M_root()->_M_parent = __t._M_end(); - __t._M_impl._M_node_count = _M_impl._M_node_count; - - _M_impl._M_reset(); - } + __t._M_impl._M_move_data(_M_impl); else { std::swap(_M_root(),__t._M_root()); [-- Attachment #3: rbtree_simplifications.patch --] [-- Type: text/x-patch, Size: 3561 bytes --] diff --git a/libstdc++-v3/include/bits/stl_tree.h b/libstdc++-v3/include/bits/stl_tree.h index 8de5e31..1bfbfa7 100644 --- a/libstdc++-v3/include/bits/stl_tree.h +++ b/libstdc++-v3/include/bits/stl_tree.h @@ -861,11 +861,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Link_type _M_copy(_Const_Link_type __x, _Base_ptr __p, _NodeGen&); + template<typename _NodeGen> + _Link_type + _M_copy(const _Rb_tree& __x, _NodeGen& __gen) + { + _Link_type __root = _M_copy(__x._M_begin(), _M_end(), __gen); + _M_leftmost() = _S_minimum(__root); + _M_rightmost() = _S_maximum(__root); + _M_impl._M_node_count = __x._M_impl._M_node_count; + return __root; + } + _Link_type - _M_copy(_Const_Link_type __x, _Base_ptr __p) + _M_copy(const _Rb_tree& __x) { _Alloc_node __an(*this); - return _M_copy(__x, __p, __an); + return _M_copy(__x, __an); } void @@ -903,12 +914,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _M_impl(__x._M_impl) { if (__x._M_root() != 0) - { - _M_root() = _M_copy(__x._M_begin(), _M_end()); - _M_leftmost() = _S_minimum(_M_root()); - _M_rightmost() = _S_maximum(_M_root()); - _M_impl._M_node_count = __x._M_impl._M_node_count; - } + _M_root() = _M_copy(__x); } #if __cplusplus >= 201103L @@ -920,12 +926,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _M_impl(__x._M_impl._M_key_compare, _Node_allocator(__a)) { if (__x._M_root() != nullptr) - { - _M_root() = _M_copy(__x._M_begin(), _M_end()); - _M_leftmost() = _S_minimum(_M_root()); - _M_rightmost() = _S_maximum(_M_root()); - _M_impl._M_node_count = __x._M_impl._M_node_count; - } + _M_root() = _M_copy(__x); } _Rb_tree(_Rb_tree&&) = default; @@ -1595,10 +1596,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION auto& __val = const_cast<value_type&>(__cval); return __an(std::move_if_noexcept(__val)); }; - _M_root() = _M_copy(__x._M_begin(), _M_end(), __lbd); - _M_leftmost() = _S_minimum(_M_root()); - _M_rightmost() = _S_maximum(_M_root()); - _M_impl._M_node_count = __x._M_impl._M_node_count; + _M_root() = _M_copy(__x, __lbd); } } @@ -1636,10 +1634,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION auto& __val = const_cast<value_type&>(__cval); return __roan(std::move_if_noexcept(__val)); }; - _M_root() = _M_copy(__x._M_begin(), _M_end(), __lbd); - _M_leftmost() = _S_minimum(_M_root()); - _M_rightmost() = _S_maximum(_M_root()); - _M_impl._M_node_count = __x._M_impl._M_node_count; + _M_root() = _M_copy(__x, __lbd); __x.clear(); } } @@ -1653,10 +1648,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION && is_nothrow_move_assignable<_Compare>::value) { _M_impl._M_key_compare = std::move(__x._M_impl._M_key_compare); - constexpr bool __move_storage = - _Alloc_traits::_S_propagate_on_move_assign() - || _Alloc_traits::_S_always_equal(); - _M_move_assign(__x, __bool_constant<__move_storage>()); + _M_move_assign(__x, __bool_constant<_Alloc_traits::_S_nothrow_move()>()); return *this; } @@ -1716,12 +1708,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_impl._M_reset(); _M_impl._M_key_compare = __x._M_impl._M_key_compare; if (__x._M_root() != 0) - { - _M_root() = _M_copy(__x._M_begin(), _M_end(), __roan); - _M_leftmost() = _S_minimum(_M_root()); - _M_rightmost() = _S_maximum(_M_root()); - _M_impl._M_node_count = __x._M_impl._M_node_count; - } + _M_root() = _M_copy(__x, __roan); } return *this; ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Default associative containers constructors/destructor/assignment 2016-11-20 18:14 ` François Dumont @ 2016-12-06 11:54 ` Jonathan Wakely 2016-12-08 20:40 ` François Dumont 0 siblings, 1 reply; 7+ messages in thread From: Jonathan Wakely @ 2016-12-06 11:54 UTC (permalink / raw) To: François Dumont; +Cc: libstdc++, gcc-patches On 20/11/16 19:14 +0100, François Dumont wrote: >Talking about _M_color I just realize that move semantic doesn't copy >_M_color. Shouldn't we capture it along with all the other _M_header >information ? Is it needed? I think the current code does the right thing, doesn't it? I'd prefer not to change it without a testcase showing why we need to. >>Am I missing some other reason that inheritance is used here? > >The purpose of this patch is to rely more on compiler especially in >regards to the noexcept qualifications. This is why I started >isolating each ressource in its own class in charge of its >specificities. And I leave to the compiler the work of combining those >types. However I also wanted to limit the impact of this patch on the >_Rb_tree and still be able to use things like >this->_M_impl._M_node_count or this->_M_impl._M_header. So the usage >of inheritance. OK. I think I've convinced myself that using inheritance here can't alter the layout in any way (I was worried about reuse of tail padding, for example). >Tested under x86_64 Linux, ok to commit ? OK for trunk. >For info, I would like to propose another bunch of simplifications of >_Rb_tree, see other patch attached. Do you want me to propose it in >another mail ? The patch is safe enough for stage 3, so please go ahead and commit that one too, but we need to stop refactoring these things now. Any more changes like this need to wait for stage 1. Thanks for this work. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Default associative containers constructors/destructor/assignment 2016-12-06 11:54 ` Jonathan Wakely @ 2016-12-08 20:40 ` François Dumont 2016-12-09 9:34 ` Jonathan Wakely 0 siblings, 1 reply; 7+ messages in thread From: François Dumont @ 2016-12-08 20:40 UTC (permalink / raw) To: Jonathan Wakely; +Cc: libstdc++, gcc-patches On 06/12/2016 12:54, Jonathan Wakely wrote: > On 20/11/16 19:14 +0100, François Dumont wrote: >> Talking about _M_color I just realize that move semantic doesn't copy >> _M_color. Shouldn't we capture it along with all the other _M_header >> information ? > > Is it needed? I think the current code does the right thing, doesn't > it? I'd prefer not to change it without a testcase showing why we need > to. I was hoping you knew it. So I kept it unchanged and will add to my TODO to check this. Committed now. François ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Default associative containers constructors/destructor/assignment 2016-12-08 20:40 ` François Dumont @ 2016-12-09 9:34 ` Jonathan Wakely 0 siblings, 0 replies; 7+ messages in thread From: Jonathan Wakely @ 2016-12-09 9:34 UTC (permalink / raw) To: François Dumont; +Cc: libstdc++, gcc-patches On 08/12/16 21:40 +0100, François Dumont wrote: >On 06/12/2016 12:54, Jonathan Wakely wrote: >>On 20/11/16 19:14 +0100, François Dumont wrote: >>>Talking about _M_color I just realize that move semantic doesn't >>>copy _M_color. Shouldn't we capture it along with all the other >>>_M_header information ? >> >>Is it needed? I think the current code does the right thing, doesn't >>it? I'd prefer not to change it without a testcase showing why we need >>to. > >I was hoping you knew it. So I kept it unchanged and will add to my >TODO to check this. I think it's OK already :-) >Committed now. Thanks! ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2016-12-09 9:34 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2016-10-28 19:43 Default associative containers constructors/destructor/assignment François Dumont [not found] ` <24dd5816-4092-00f4-49b8-189d5ea68b52@gmail.com> 2016-11-14 20:35 ` François Dumont 2016-11-17 17:52 ` Jonathan Wakely 2016-11-20 18:14 ` François Dumont 2016-12-06 11:54 ` Jonathan Wakely 2016-12-08 20:40 ` François Dumont 2016-12-09 9:34 ` 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).