* std::vector<bool> fix & enhancements
@ 2018-10-30 9:12 François Dumont
2018-10-31 1:42 ` François Dumont
2018-10-31 2:28 ` Jonathan Wakely
0 siblings, 2 replies; 4+ messages in thread
From: François Dumont @ 2018-10-30 9:12 UTC (permalink / raw)
To: libstdc++, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 3074 bytes --]
Following Marc Glisse change to ignore _M_start offset I wanted to go a
little step further and just remove it in _GLIBCXX_INLINE_VERSION mode.
I also fix a regression we already fixed on mainstream std::vector
regarding noexcept qualification of move constructor with allocator.
And I implemented the same optimizations than in std::vector for
allocators always comparing equals and for the std::swap operation.
I also avoid re-implementing in vector::operator[] the same code already
implemented in iterator::operator[] but this one should perhaps go in a
different commit.
   * include/bits/stl_bvector.h
   [_GLIBCXX_INLINE_VERSION](_Bvector_impl_data::_M_start): Define as
   _Bit_type*.
   (_Bvector_impl_data(const _Bvector_impl_data&)): New.
   (_Bvector_impl_data(_Bvector_impl_data&&)): Delegate to latter.
   (_Bvector_impl_data::operator=(const _Bvector_impl_data&)): New.
(_Bvector_impl_data::_M_move_data(_Bvector_impl_data&&)): Use latter.
   (_Bvector_impl_data::_M_reset()): Likewise.
   (_Bvector_impl_data::_M_begin()): New.
   (_Bvector_impl_data::_M_cbegin()): New.
   (_Bvector_impl_data::_M_start_p()): New.
   (_Bvector_impl_data::_M_set_start(_Bit_type*)): New.
   (_Bvector_impl_data::_M_swap_data): New.
   (_Bvector_impl::_Bvector_impl(_Bvector_impl&&)): Implement explicitely.
   (_Bvector_impl::_Bvector_impl(_Bit_alloc_type&&, _Bvector_impl&&)):
New.
   (_Bvector_base::_Bvector_base(_Bvector_base&&, const allocator_type&)):
   New.
   (_Bvector_base::_M_deallocate()): Adapt.
   (vector::vector(const vector&, const allocator_type&)): Adapt.
   (vector::vector(vector&&, const allocator_type&, true_type)): New.
   (vector::vector(vector&&, const allocator_type&, false_type)): New.
   (vector::vector(vector&&, const allocator_type&)): Use latters.
   (vector::vector(const vector&, const allocator_type&)): Adapt.
   (vector::begin()): Adapt.
   (vector::cbegin()): Adapt.
   (vector::operator[](size_type)): Use iterator operator[].
   (vector::swap(vector&)): Adapt.
   (vector::flip()): Adapt.
   (vector::_M_initialize(size_type)): Adapt.
   (vector::_M_initialize_value(bool)): Adapt.
   * include/bits/vector.tcc:
   (vector<bool>::_M_reallocate(size_type)): Adapt.
   (vector<bool>::_M_fill_insert(iterator, size_type, bool)): Adapt.
   (vector<bool>::_M_insert_range<_FwdIter>(iterator, _FwdIter, _FwdIter
   std::forward_iterator_tag)): Adapt.
   (vector<bool>::_M_insert_aux(iterator, bool)): Adapt.
   (std::hash<std::vector<bool>>::operator()): Adapt.
   * testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc:
   Add check.
Tested under Linux x86_64.
Ok to commit ?
François
[-- Attachment #2: bvector.patch --]
[-- Type: text/x-patch, Size: 12157 bytes --]
diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h
index 8fbef7a1a3a..81b4a75236d 100644
--- a/libstdc++-v3/include/bits/stl_bvector.h
+++ b/libstdc++-v3/include/bits/stl_bvector.h
@@ -437,7 +437,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
struct _Bvector_impl_data
{
+#if !_GLIBCXX_INLINE_VERSION
_Bit_iterator _M_start;
+#else
+ _Bit_type* _M_start;
+#endif
_Bit_iterator _M_finish;
_Bit_pointer _M_end_of_storage;
@@ -447,32 +451,74 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#if __cplusplus >= 201103L
_Bvector_impl_data(_Bvector_impl_data&& __x) noexcept
- : _M_start(__x._M_start), _M_finish(__x._M_finish)
- , _M_end_of_storage(__x._M_end_of_storage)
+ : _Bvector_impl_data(__x)
{ __x._M_reset(); }
+ _Bvector_impl_data(const _Bvector_impl_data&) = default;
+ _Bvector_impl_data&
+ operator=(const _Bvector_impl_data&) = default;
+
void
_M_move_data(_Bvector_impl_data&& __x) noexcept
{
- this->_M_start = __x._M_start;
- this->_M_finish = __x._M_finish;
- this->_M_end_of_storage = __x._M_end_of_storage;
+ *this = __x;
__x._M_reset();
}
+#else
+ _Bvector_impl_data(const _Bvector_impl_data& __x)
+ : _M_start(__x._M_start), _M_finish(__x._M_finish)
+ , _M_end_of_storage(__x._M_end_of_storage)
+ { }
+
+ _Bvector_impl_data&
+ operator=(const _Bvector_impl_data& __x)
+ {
+ _M_start = __x._M_start;
+ _M_finish = __x._M_finish;
+ _M_end_of_storage = __x._M_end_of_storage;
+ }
+#endif
+
+ _Bit_iterator
+ _M_begin() const _GLIBCXX_NOEXCEPT
+ { return _Bit_iterator(_M_start_p(), 0); }
+
+ _Bit_const_iterator
+ _M_cbegin() const _GLIBCXX_NOEXCEPT
+ { return _Bit_const_iterator(_M_start_p(), 0); }
+
+ _Bit_type*
+ _M_start_p() const _GLIBCXX_NOEXCEPT
+#if !_GLIBCXX_INLINE_VERSION
+ { return _M_start._M_p; }
+#else
+ { return _M_start; }
+#endif
+
+ void
+ _M_set_start(_Bit_type* __p) _GLIBCXX_NOEXCEPT
+#if !_GLIBCXX_INLINE_VERSION
+ { _M_start._M_p = __p; }
+#else
+ { _M_start = __p; }
#endif
void
_M_reset() _GLIBCXX_NOEXCEPT
+ { *this = _Bvector_impl_data(); }
+
+ void
+ _M_swap_data(_Bvector_impl_data& __x) _GLIBCXX_NOEXCEPT
{
- _M_start = _M_finish = _Bit_iterator();
- _M_end_of_storage = _Bit_pointer();
+ // Do not use std::swap(_M_start, __x._M_start), etc as it loses
+ // information used by TBAA.
+ std::swap(*this, __x);
}
};
struct _Bvector_impl
: public _Bit_alloc_type, public _Bvector_impl_data
{
- public:
_Bvector_impl()
_GLIBCXX_NOEXCEPT_IF( noexcept(_Bit_alloc_type()) )
: _Bit_alloc_type()
@@ -483,7 +529,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{ }
#if __cplusplus >= 201103L
- _Bvector_impl(_Bvector_impl&&) = default;
+ // Not defaulted, to enforce noexcept(true) even when
+ // !is_nothrow_move_constructible<_Bit_alloc_type>.
+ _Bvector_impl(_Bvector_impl&& __x) noexcept
+ : _Bit_alloc_type(std::move(__x)), _Bvector_impl_data(std::move(__x))
+ { }
+
+ _Bvector_impl(_Bit_alloc_type&& __a, _Bvector_impl&& __x) noexcept
+ : _Bit_alloc_type(std::move(__a)), _Bvector_impl_data(std::move(__x))
+ { }
#endif
_Bit_type*
@@ -521,6 +575,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#if __cplusplus >= 201103L
_Bvector_base(_Bvector_base&&) = default;
+
+ _Bvector_base(_Bvector_base&& __x, const allocator_type& __a)
+ : _M_impl(_Bit_alloc_type(__a), std::move(__x._M_impl))
+ { }
#endif
~_Bvector_base()
@@ -536,9 +594,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
void
_M_deallocate()
{
- if (_M_impl._M_start._M_p)
+ if (_M_impl._M_start_p())
{
- const size_t __n = _M_impl._M_end_addr() - _M_impl._M_start._M_p;
+ const size_t __n = _M_impl._M_end_addr() - _M_impl._M_start_p();
_Bit_alloc_traits::deallocate(_M_impl,
_M_impl._M_end_of_storage - __n,
__n);
@@ -657,14 +715,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
: _Base(_Bit_alloc_traits::_S_select_on_copy(__x._M_get_Bit_allocator()))
{
_M_initialize(__x.size());
- _M_copy_aligned(__x.begin(), __x.end(), this->_M_impl._M_start);
+ _M_copy_aligned(__x.begin(), __x.end(), begin());
}
#if __cplusplus >= 201103L
vector(vector&&) = default;
- vector(vector&& __x, const allocator_type& __a)
- noexcept(_Bit_alloc_traits::_S_always_equal())
+ private:
+ vector(vector&& __x, const allocator_type& __a, true_type) noexcept
+ : _Base(std::move(__x), __a)
+ { }
+
+ vector(vector&& __x, const allocator_type& __a, false_type)
: _Base(__a)
{
if (__x.get_allocator() == __a)
@@ -677,11 +739,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
}
+ public:
+ vector(vector&& __x, const allocator_type& __a)
+ noexcept(_Bit_alloc_traits::_S_always_equal())
+ : vector(std::move(__x), __a,
+ typename _Bit_alloc_traits::is_always_equal{})
+ { }
+
vector(const vector& __x, const allocator_type& __a)
: _Base(__a)
{
_M_initialize(__x.size());
- _M_copy_aligned(__x.begin(), __x.end(), this->_M_impl._M_start);
+ _M_copy_aligned(__x.begin(), __x.end(), begin());
}
vector(initializer_list<bool> __l,
@@ -809,11 +878,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator
begin() _GLIBCXX_NOEXCEPT
- { return iterator(this->_M_impl._M_start._M_p, 0); }
+ { return this->_M_impl._M_begin(); }
const_iterator
begin() const _GLIBCXX_NOEXCEPT
- { return const_iterator(this->_M_impl._M_start._M_p, 0); }
+ { return this->_M_impl._M_cbegin(); }
iterator
end() _GLIBCXX_NOEXCEPT
@@ -842,7 +911,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#if __cplusplus >= 201103L
const_iterator
cbegin() const noexcept
- { return const_iterator(this->_M_impl._M_start._M_p, 0); }
+ { return this->_M_impl._M_cbegin(); }
const_iterator
cend() const noexcept
@@ -884,17 +953,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
reference
operator[](size_type __n)
- {
- return *iterator(this->_M_impl._M_start._M_p
- + __n / int(_S_word_bit), __n % int(_S_word_bit));
- }
+ { return begin()[__n]; }
const_reference
operator[](size_type __n) const
- {
- return *const_iterator(this->_M_impl._M_start._M_p
- + __n / int(_S_word_bit), __n % int(_S_word_bit));
- }
+ { return begin()[__n]; }
protected:
void
@@ -961,10 +1024,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
void
swap(vector& __x) _GLIBCXX_NOEXCEPT
{
- std::swap(this->_M_impl._M_start, __x._M_impl._M_start);
- std::swap(this->_M_impl._M_finish, __x._M_impl._M_finish);
- std::swap(this->_M_impl._M_end_of_storage,
- __x._M_impl._M_end_of_storage);
+#if __cplusplus >= 201103L
+ __glibcxx_assert(_Bit_alloc_traits::propagate_on_container_swap::value
+ || _M_get_Bit_allocator() == __x._M_get_Bit_allocator());
+#endif
+ this->_M_impl._M_swap_data(__x._M_impl);
_Bit_alloc_traits::_S_on_swap(_M_get_Bit_allocator(),
__x._M_get_Bit_allocator());
}
@@ -1076,7 +1140,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
flip() _GLIBCXX_NOEXCEPT
{
_Bit_type * const __end = this->_M_impl._M_end_addr();
- for (_Bit_type * __p = this->_M_impl._M_start._M_p; __p != __end; ++__p)
+ for (_Bit_type * __p = this->_M_impl._M_start_p(); __p != __end; ++__p)
*__p = ~*__p;
}
@@ -1123,21 +1187,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{
_Bit_pointer __q = this->_M_allocate(__n);
this->_M_impl._M_end_of_storage = __q + _S_nword(__n);
- this->_M_impl._M_start = iterator(std::__addressof(*__q), 0);
+ this->_M_impl._M_set_start(std::__addressof(*__q));
+ this->_M_impl._M_finish = this->_M_impl._M_begin() + difference_type(__n);
}
else
- {
- this->_M_impl._M_end_of_storage = _Bit_pointer();
- this->_M_impl._M_start = iterator(0, 0);
- }
- this->_M_impl._M_finish = this->_M_impl._M_start + difference_type(__n);
-
+ this->_M_impl._M_reset();
}
void
_M_initialize_value(bool __x)
{
- if (_Bit_type* __p = this->_M_impl._M_start._M_p)
+ if (_Bit_type* __p = this->_M_impl._M_start_p())
__builtin_memset(__p, __x ? ~0 : 0,
(this->_M_impl._M_end_addr() - __p)
* sizeof(_Bit_type));
@@ -1186,7 +1246,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{
const size_type __n = std::distance(__first, __last);
_M_initialize(__n);
- std::copy(__first, __last, this->_M_impl._M_start);
+ std::copy(__first, __last, begin());
}
#if __cplusplus < 201103L
diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc
index 8df0f4180d4..46b42e05ecd 100644
--- a/libstdc++-v3/include/bits/vector.tcc
+++ b/libstdc++-v3/include/bits/vector.tcc
@@ -822,7 +822,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __start(std::__addressof(*__q), 0);
iterator __finish(_M_copy_aligned(begin(), end(), __start));
this->_M_deallocate();
- this->_M_impl._M_start = __start;
+ this->_M_impl._M_set_start(__start._M_p);
this->_M_impl._M_finish = __finish;
this->_M_impl._M_end_of_storage = __q + _S_nword(__n);
}
@@ -853,7 +853,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
__i + difference_type(__n));
this->_M_deallocate();
this->_M_impl._M_end_of_storage = __q + _S_nword(__len);
- this->_M_impl._M_start = __start;
+ this->_M_impl._M_set_start(__start._M_p);
this->_M_impl._M_finish = __finish;
}
}
@@ -887,7 +887,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __finish = std::copy(__position, end(), __i);
this->_M_deallocate();
this->_M_impl._M_end_of_storage = __q + _S_nword(__len);
- this->_M_impl._M_start = __start;
+ this->_M_impl._M_set_start(__start._M_p);
this->_M_impl._M_finish = __finish;
}
}
@@ -916,7 +916,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __finish = std::copy(__position, end(), __i);
this->_M_deallocate();
this->_M_impl._M_end_of_storage = __q + _S_nword(__len);
- this->_M_impl._M_start = __start;
+ this->_M_impl._M_set_start(__start._M_p);
this->_M_impl._M_finish = __finish;
}
}
@@ -983,7 +983,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
if (__words)
{
const size_t __clength = __words * sizeof(_Bit_type);
- __hash = std::_Hash_impl::hash(__b._M_impl._M_start._M_p, __clength);
+ __hash = std::_Hash_impl::hash(__b._M_impl._M_start_p(), __clength);
}
const size_t __extrabits = __b.size() % _S_word_bit;
diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc
index 62429424a5f..fa2b053c66e 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc
@@ -23,4 +23,34 @@
typedef std::vector<bool> vbtype;
-static_assert(std::is_nothrow_move_constructible<vbtype>::value, "Error");
+static_assert( std::is_nothrow_move_constructible<vbtype>::value,
+ "noexcept move constructor" );
+static_assert( std::is_nothrow_constructible<vbtype,
+ vbtype&&, const typename vbtype::allocator_type&>::value,
+ "noexcept move constructor with allocator" );
+
+template<typename Type>
+ class not_noexcept_move_constructor_alloc : public std::allocator<Type>
+ {
+ public:
+ not_noexcept_move_constructor_alloc() noexcept { }
+
+ not_noexcept_move_constructor_alloc(
+ const not_noexcept_move_constructor_alloc& x) noexcept
+ : std::allocator<Type>(x)
+ { }
+
+ not_noexcept_move_constructor_alloc(
+ not_noexcept_move_constructor_alloc&& x) noexcept(false)
+ : std::allocator<Type>(std::move(x))
+ { }
+
+ template<typename _Tp1>
+ struct rebind
+ { typedef not_noexcept_move_constructor_alloc<_Tp1> other; };
+ };
+
+typedef std::vector<bool, not_noexcept_move_constructor_alloc<bool>> vbtype2;
+
+static_assert( std::is_nothrow_move_constructible<vbtype2>::value,
+ "noexcept move constructor with not noexcept alloc" );
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: std::vector<bool> fix & enhancements
2018-10-30 9:12 std::vector<bool> fix & enhancements François Dumont
@ 2018-10-31 1:42 ` François Dumont
2018-10-31 2:28 ` Jonathan Wakely
1 sibling, 0 replies; 4+ messages in thread
From: François Dumont @ 2018-10-31 1:42 UTC (permalink / raw)
To: libstdc++, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 3343 bytes --]
Running tests in C++98 mode show that I had forgotten a 'return *this;'
in _Bvector_impl_data::operator=.
So here is the patch again.
On 10/30/18 7:28 AM, François Dumont wrote:
> Following Marc Glisse change to ignore _M_start offset I wanted to go
> a little step further and just remove it in _GLIBCXX_INLINE_VERSION mode.
>
> I also fix a regression we already fixed on mainstream std::vector
> regarding noexcept qualification of move constructor with allocator.
>
> And I implemented the same optimizations than in std::vector for
> allocators always comparing equals and for the std::swap operation.
>
> I also avoid re-implementing in vector::operator[] the same code
> already implemented in iterator::operator[] but this one should
> perhaps go in a different commit.
>
>
> Â Â Â * include/bits/stl_bvector.h
> Â Â Â [_GLIBCXX_INLINE_VERSION](_Bvector_impl_data::_M_start): Define as
> Â Â Â _Bit_type*.
> Â Â Â (_Bvector_impl_data(const _Bvector_impl_data&)): New.
> Â Â Â (_Bvector_impl_data(_Bvector_impl_data&&)): Delegate to latter.
> Â Â Â (_Bvector_impl_data::operator=(const _Bvector_impl_data&)): New.
> (_Bvector_impl_data::_M_move_data(_Bvector_impl_data&&)): Use latter.
> Â Â Â (_Bvector_impl_data::_M_reset()): Likewise.
> Â Â Â (_Bvector_impl_data::_M_begin()): New.
> Â Â Â (_Bvector_impl_data::_M_cbegin()): New.
> Â Â Â (_Bvector_impl_data::_M_start_p()): New.
> Â Â Â (_Bvector_impl_data::_M_set_start(_Bit_type*)): New.
> Â Â Â (_Bvector_impl_data::_M_swap_data): New.
> Â Â Â (_Bvector_impl::_Bvector_impl(_Bvector_impl&&)): Implement
> explicitely.
> Â Â Â (_Bvector_impl::_Bvector_impl(_Bit_alloc_type&&,
> _Bvector_impl&&)): New.
> Â Â Â (_Bvector_base::_Bvector_base(_Bvector_base&&, const
> allocator_type&)):
> Â Â Â New.
> Â Â Â (_Bvector_base::_M_deallocate()): Adapt.
> Â Â Â (vector::vector(const vector&, const allocator_type&)): Adapt.
> Â Â Â (vector::vector(vector&&, const allocator_type&, true_type)): New.
> Â Â Â (vector::vector(vector&&, const allocator_type&, false_type)): New.
> Â Â Â (vector::vector(vector&&, const allocator_type&)): Use latters.
> Â Â Â (vector::vector(const vector&, const allocator_type&)): Adapt.
> Â Â Â (vector::begin()): Adapt.
> Â Â Â (vector::cbegin()): Adapt.
> Â Â Â (vector::operator[](size_type)): Use iterator operator[].
> Â Â Â (vector::swap(vector&)): Adapt.
> Â Â Â (vector::flip()): Adapt.
> Â Â Â (vector::_M_initialize(size_type)): Adapt.
> Â Â Â (vector::_M_initialize_value(bool)): Adapt.
> Â Â Â * include/bits/vector.tcc:
> Â Â Â (vector<bool>::_M_reallocate(size_type)): Adapt.
> Â Â Â (vector<bool>::_M_fill_insert(iterator, size_type, bool)): Adapt.
> Â Â Â (vector<bool>::_M_insert_range<_FwdIter>(iterator, _FwdIter, _FwdIter
> Â Â Â std::forward_iterator_tag)): Adapt.
> Â Â Â (vector<bool>::_M_insert_aux(iterator, bool)): Adapt.
> Â Â Â (std::hash<std::vector<bool>>::operator()): Adapt.
> Â Â Â *
> testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc:
> Â Â Â Add check.
>
> Tested under Linux x86_64.
>
> Ok to commit ?
>
> François
>
[-- Attachment #2: bvector.patch --]
[-- Type: text/x-patch, Size: 12199 bytes --]
diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h
index 0bcfd19fd3e..dd0f00fe07f 100644
--- a/libstdc++-v3/include/bits/stl_bvector.h
+++ b/libstdc++-v3/include/bits/stl_bvector.h
@@ -437,7 +437,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
struct _Bvector_impl_data
{
+#if !_GLIBCXX_INLINE_VERSION
_Bit_iterator _M_start;
+#else
+ _Bit_type* _M_start;
+#endif
_Bit_iterator _M_finish;
_Bit_pointer _M_end_of_storage;
@@ -447,32 +451,75 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#if __cplusplus >= 201103L
_Bvector_impl_data(_Bvector_impl_data&& __x) noexcept
- : _M_start(__x._M_start), _M_finish(__x._M_finish)
- , _M_end_of_storage(__x._M_end_of_storage)
+ : _Bvector_impl_data(__x)
{ __x._M_reset(); }
+ _Bvector_impl_data(const _Bvector_impl_data&) = default;
+ _Bvector_impl_data&
+ operator=(const _Bvector_impl_data&) = default;
+
void
_M_move_data(_Bvector_impl_data&& __x) noexcept
{
- this->_M_start = __x._M_start;
- this->_M_finish = __x._M_finish;
- this->_M_end_of_storage = __x._M_end_of_storage;
+ *this = __x;
__x._M_reset();
}
+#else
+ _Bvector_impl_data(const _Bvector_impl_data& __x)
+ : _M_start(__x._M_start), _M_finish(__x._M_finish)
+ , _M_end_of_storage(__x._M_end_of_storage)
+ { }
+
+ _Bvector_impl_data&
+ operator=(const _Bvector_impl_data& __x)
+ {
+ _M_start = __x._M_start;
+ _M_finish = __x._M_finish;
+ _M_end_of_storage = __x._M_end_of_storage;
+ return *this;
+ }
+#endif
+
+ _Bit_iterator
+ _M_begin() const _GLIBCXX_NOEXCEPT
+ { return _Bit_iterator(_M_start_p(), 0); }
+
+ _Bit_const_iterator
+ _M_cbegin() const _GLIBCXX_NOEXCEPT
+ { return _Bit_const_iterator(_M_start_p(), 0); }
+
+ _Bit_type*
+ _M_start_p() const _GLIBCXX_NOEXCEPT
+#if !_GLIBCXX_INLINE_VERSION
+ { return _M_start._M_p; }
+#else
+ { return _M_start; }
+#endif
+
+ void
+ _M_set_start(_Bit_type* __p) _GLIBCXX_NOEXCEPT
+#if !_GLIBCXX_INLINE_VERSION
+ { _M_start._M_p = __p; }
+#else
+ { _M_start = __p; }
#endif
void
_M_reset() _GLIBCXX_NOEXCEPT
+ { *this = _Bvector_impl_data(); }
+
+ void
+ _M_swap_data(_Bvector_impl_data& __x) _GLIBCXX_NOEXCEPT
{
- _M_start = _M_finish = _Bit_iterator();
- _M_end_of_storage = _Bit_pointer();
+ // Do not use std::swap(_M_start, __x._M_start), etc as it loses
+ // information used by TBAA.
+ std::swap(*this, __x);
}
};
struct _Bvector_impl
: public _Bit_alloc_type, public _Bvector_impl_data
{
- public:
_Bvector_impl() _GLIBCXX_NOEXCEPT_IF(
is_nothrow_default_constructible<_Bit_alloc_type>::value)
: _Bit_alloc_type()
@@ -483,7 +530,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{ }
#if __cplusplus >= 201103L
- _Bvector_impl(_Bvector_impl&&) = default;
+ // Not defaulted, to enforce noexcept(true) even when
+ // !is_nothrow_move_constructible<_Bit_alloc_type>.
+ _Bvector_impl(_Bvector_impl&& __x) noexcept
+ : _Bit_alloc_type(std::move(__x)), _Bvector_impl_data(std::move(__x))
+ { }
+
+ _Bvector_impl(_Bit_alloc_type&& __a, _Bvector_impl&& __x) noexcept
+ : _Bit_alloc_type(std::move(__a)), _Bvector_impl_data(std::move(__x))
+ { }
#endif
_Bit_type*
@@ -521,6 +576,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#if __cplusplus >= 201103L
_Bvector_base(_Bvector_base&&) = default;
+
+ _Bvector_base(_Bvector_base&& __x, const allocator_type& __a)
+ : _M_impl(_Bit_alloc_type(__a), std::move(__x._M_impl))
+ { }
#endif
~_Bvector_base()
@@ -536,9 +595,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
void
_M_deallocate()
{
- if (_M_impl._M_start._M_p)
+ if (_M_impl._M_start_p())
{
- const size_t __n = _M_impl._M_end_addr() - _M_impl._M_start._M_p;
+ const size_t __n = _M_impl._M_end_addr() - _M_impl._M_start_p();
_Bit_alloc_traits::deallocate(_M_impl,
_M_impl._M_end_of_storage - __n,
__n);
@@ -657,14 +716,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
: _Base(_Bit_alloc_traits::_S_select_on_copy(__x._M_get_Bit_allocator()))
{
_M_initialize(__x.size());
- _M_copy_aligned(__x.begin(), __x.end(), this->_M_impl._M_start);
+ _M_copy_aligned(__x.begin(), __x.end(), begin());
}
#if __cplusplus >= 201103L
vector(vector&&) = default;
- vector(vector&& __x, const allocator_type& __a)
- noexcept(_Bit_alloc_traits::_S_always_equal())
+ private:
+ vector(vector&& __x, const allocator_type& __a, true_type) noexcept
+ : _Base(std::move(__x), __a)
+ { }
+
+ vector(vector&& __x, const allocator_type& __a, false_type)
: _Base(__a)
{
if (__x.get_allocator() == __a)
@@ -677,11 +740,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
}
+ public:
+ vector(vector&& __x, const allocator_type& __a)
+ noexcept(_Bit_alloc_traits::_S_always_equal())
+ : vector(std::move(__x), __a,
+ typename _Bit_alloc_traits::is_always_equal{})
+ { }
+
vector(const vector& __x, const allocator_type& __a)
: _Base(__a)
{
_M_initialize(__x.size());
- _M_copy_aligned(__x.begin(), __x.end(), this->_M_impl._M_start);
+ _M_copy_aligned(__x.begin(), __x.end(), begin());
}
vector(initializer_list<bool> __l,
@@ -809,11 +879,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator
begin() _GLIBCXX_NOEXCEPT
- { return iterator(this->_M_impl._M_start._M_p, 0); }
+ { return this->_M_impl._M_begin(); }
const_iterator
begin() const _GLIBCXX_NOEXCEPT
- { return const_iterator(this->_M_impl._M_start._M_p, 0); }
+ { return this->_M_impl._M_cbegin(); }
iterator
end() _GLIBCXX_NOEXCEPT
@@ -842,7 +912,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#if __cplusplus >= 201103L
const_iterator
cbegin() const noexcept
- { return const_iterator(this->_M_impl._M_start._M_p, 0); }
+ { return this->_M_impl._M_cbegin(); }
const_iterator
cend() const noexcept
@@ -884,17 +954,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
reference
operator[](size_type __n)
- {
- return *iterator(this->_M_impl._M_start._M_p
- + __n / int(_S_word_bit), __n % int(_S_word_bit));
- }
+ { return begin()[__n]; }
const_reference
operator[](size_type __n) const
- {
- return *const_iterator(this->_M_impl._M_start._M_p
- + __n / int(_S_word_bit), __n % int(_S_word_bit));
- }
+ { return begin()[__n]; }
protected:
void
@@ -961,10 +1025,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
void
swap(vector& __x) _GLIBCXX_NOEXCEPT
{
- std::swap(this->_M_impl._M_start, __x._M_impl._M_start);
- std::swap(this->_M_impl._M_finish, __x._M_impl._M_finish);
- std::swap(this->_M_impl._M_end_of_storage,
- __x._M_impl._M_end_of_storage);
+#if __cplusplus >= 201103L
+ __glibcxx_assert(_Bit_alloc_traits::propagate_on_container_swap::value
+ || _M_get_Bit_allocator() == __x._M_get_Bit_allocator());
+#endif
+ this->_M_impl._M_swap_data(__x._M_impl);
_Bit_alloc_traits::_S_on_swap(_M_get_Bit_allocator(),
__x._M_get_Bit_allocator());
}
@@ -1076,7 +1141,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
flip() _GLIBCXX_NOEXCEPT
{
_Bit_type * const __end = this->_M_impl._M_end_addr();
- for (_Bit_type * __p = this->_M_impl._M_start._M_p; __p != __end; ++__p)
+ for (_Bit_type * __p = this->_M_impl._M_start_p(); __p != __end; ++__p)
*__p = ~*__p;
}
@@ -1123,21 +1188,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{
_Bit_pointer __q = this->_M_allocate(__n);
this->_M_impl._M_end_of_storage = __q + _S_nword(__n);
- this->_M_impl._M_start = iterator(std::__addressof(*__q), 0);
+ this->_M_impl._M_set_start(std::__addressof(*__q));
+ this->_M_impl._M_finish = this->_M_impl._M_begin() + difference_type(__n);
}
else
- {
- this->_M_impl._M_end_of_storage = _Bit_pointer();
- this->_M_impl._M_start = iterator(0, 0);
- }
- this->_M_impl._M_finish = this->_M_impl._M_start + difference_type(__n);
-
+ this->_M_impl._M_reset();
}
void
_M_initialize_value(bool __x)
{
- if (_Bit_type* __p = this->_M_impl._M_start._M_p)
+ if (_Bit_type* __p = this->_M_impl._M_start_p())
__builtin_memset(__p, __x ? ~0 : 0,
(this->_M_impl._M_end_addr() - __p)
* sizeof(_Bit_type));
@@ -1186,7 +1247,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{
const size_type __n = std::distance(__first, __last);
_M_initialize(__n);
- std::copy(__first, __last, this->_M_impl._M_start);
+ std::copy(__first, __last, begin());
}
#if __cplusplus < 201103L
diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc
index 8df0f4180d4..46b42e05ecd 100644
--- a/libstdc++-v3/include/bits/vector.tcc
+++ b/libstdc++-v3/include/bits/vector.tcc
@@ -822,7 +822,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __start(std::__addressof(*__q), 0);
iterator __finish(_M_copy_aligned(begin(), end(), __start));
this->_M_deallocate();
- this->_M_impl._M_start = __start;
+ this->_M_impl._M_set_start(__start._M_p);
this->_M_impl._M_finish = __finish;
this->_M_impl._M_end_of_storage = __q + _S_nword(__n);
}
@@ -853,7 +853,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
__i + difference_type(__n));
this->_M_deallocate();
this->_M_impl._M_end_of_storage = __q + _S_nword(__len);
- this->_M_impl._M_start = __start;
+ this->_M_impl._M_set_start(__start._M_p);
this->_M_impl._M_finish = __finish;
}
}
@@ -887,7 +887,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __finish = std::copy(__position, end(), __i);
this->_M_deallocate();
this->_M_impl._M_end_of_storage = __q + _S_nword(__len);
- this->_M_impl._M_start = __start;
+ this->_M_impl._M_set_start(__start._M_p);
this->_M_impl._M_finish = __finish;
}
}
@@ -916,7 +916,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __finish = std::copy(__position, end(), __i);
this->_M_deallocate();
this->_M_impl._M_end_of_storage = __q + _S_nword(__len);
- this->_M_impl._M_start = __start;
+ this->_M_impl._M_set_start(__start._M_p);
this->_M_impl._M_finish = __finish;
}
}
@@ -983,7 +983,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
if (__words)
{
const size_t __clength = __words * sizeof(_Bit_type);
- __hash = std::_Hash_impl::hash(__b._M_impl._M_start._M_p, __clength);
+ __hash = std::_Hash_impl::hash(__b._M_impl._M_start_p(), __clength);
}
const size_t __extrabits = __b.size() % _S_word_bit;
diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc
index 62429424a5f..fa2b053c66e 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc
@@ -23,4 +23,34 @@
typedef std::vector<bool> vbtype;
-static_assert(std::is_nothrow_move_constructible<vbtype>::value, "Error");
+static_assert( std::is_nothrow_move_constructible<vbtype>::value,
+ "noexcept move constructor" );
+static_assert( std::is_nothrow_constructible<vbtype,
+ vbtype&&, const typename vbtype::allocator_type&>::value,
+ "noexcept move constructor with allocator" );
+
+template<typename Type>
+ class not_noexcept_move_constructor_alloc : public std::allocator<Type>
+ {
+ public:
+ not_noexcept_move_constructor_alloc() noexcept { }
+
+ not_noexcept_move_constructor_alloc(
+ const not_noexcept_move_constructor_alloc& x) noexcept
+ : std::allocator<Type>(x)
+ { }
+
+ not_noexcept_move_constructor_alloc(
+ not_noexcept_move_constructor_alloc&& x) noexcept(false)
+ : std::allocator<Type>(std::move(x))
+ { }
+
+ template<typename _Tp1>
+ struct rebind
+ { typedef not_noexcept_move_constructor_alloc<_Tp1> other; };
+ };
+
+typedef std::vector<bool, not_noexcept_move_constructor_alloc<bool>> vbtype2;
+
+static_assert( std::is_nothrow_move_constructible<vbtype2>::value,
+ "noexcept move constructor with not noexcept alloc" );
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: std::vector<bool> fix & enhancements
2018-10-30 9:12 std::vector<bool> fix & enhancements François Dumont
2018-10-31 1:42 ` François Dumont
@ 2018-10-31 2:28 ` Jonathan Wakely
2018-10-31 17:58 ` François Dumont
1 sibling, 1 reply; 4+ messages in thread
From: Jonathan Wakely @ 2018-10-31 2:28 UTC (permalink / raw)
To: François Dumont; +Cc: libstdc++, gcc-patches
On 30/10/18 07:28 +0100, François Dumont wrote:
>Following Marc Glisse change to ignore _M_start offset I wanted to go
>a little step further and just remove it in _GLIBCXX_INLINE_VERSION
>mode.
>
>I also fix a regression we already fixed on mainstream std::vector
>regarding noexcept qualification of move constructor with allocator.
>
>And I implemented the same optimizations than in std::vector for
>allocators always comparing equals and for the std::swap operation.
>
>I also avoid re-implementing in vector::operator[] the same code
>already implemented in iterator::operator[] but this one should
>perhaps go in a different commit.
>
>
>Â Â Â * include/bits/stl_bvector.h
>Â Â Â [_GLIBCXX_INLINE_VERSION](_Bvector_impl_data::_M_start): Define as
>Â Â Â _Bit_type*.
>Â Â Â (_Bvector_impl_data(const _Bvector_impl_data&)): New.
>Â Â Â (_Bvector_impl_data(_Bvector_impl_data&&)): Delegate to latter.
>Â Â Â (_Bvector_impl_data::operator=(const _Bvector_impl_data&)): New.
>(_Bvector_impl_data::_M_move_data(_Bvector_impl_data&&)): Use latter.
>Â Â Â (_Bvector_impl_data::_M_reset()): Likewise.
>Â Â Â (_Bvector_impl_data::_M_begin()): New.
>Â Â Â (_Bvector_impl_data::_M_cbegin()): New.
>Â Â Â (_Bvector_impl_data::_M_start_p()): New.
>Â Â Â (_Bvector_impl_data::_M_set_start(_Bit_type*)): New.
>Â Â Â (_Bvector_impl_data::_M_swap_data): New.
>Â Â Â (_Bvector_impl::_Bvector_impl(_Bvector_impl&&)): Implement explicitely.
>Â Â Â (_Bvector_impl::_Bvector_impl(_Bit_alloc_type&&,
>_Bvector_impl&&)): New.
>Â Â Â (_Bvector_base::_Bvector_base(_Bvector_base&&, const allocator_type&)):
>Â Â Â New.
>Â Â Â (_Bvector_base::_M_deallocate()): Adapt.
>Â Â Â (vector::vector(const vector&, const allocator_type&)): Adapt.
>Â Â Â (vector::vector(vector&&, const allocator_type&, true_type)): New.
>Â Â Â (vector::vector(vector&&, const allocator_type&, false_type)): New.
>Â Â Â (vector::vector(vector&&, const allocator_type&)): Use latters.
>Â Â Â (vector::vector(const vector&, const allocator_type&)): Adapt.
>Â Â Â (vector::begin()): Adapt.
>Â Â Â (vector::cbegin()): Adapt.
>Â Â Â (vector::operator[](size_type)): Use iterator operator[].
>Â Â Â (vector::swap(vector&)): Adapt.
>Â Â Â (vector::flip()): Adapt.
>Â Â Â (vector::_M_initialize(size_type)): Adapt.
>Â Â Â (vector::_M_initialize_value(bool)): Adapt.
>Â Â Â * include/bits/vector.tcc:
>Â Â Â (vector<bool>::_M_reallocate(size_type)): Adapt.
>Â Â Â (vector<bool>::_M_fill_insert(iterator, size_type, bool)): Adapt.
>Â Â Â (vector<bool>::_M_insert_range<_FwdIter>(iterator, _FwdIter, _FwdIter
>Â Â Â std::forward_iterator_tag)): Adapt.
>Â Â Â (vector<bool>::_M_insert_aux(iterator, bool)): Adapt.
>Â Â Â (std::hash<std::vector<bool>>::operator()): Adapt.
>Â Â Â * testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc:
>Â Â Â Add check.
>
>Tested under Linux x86_64.
>
>Ok to commit ?
>
>François
>
>diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h
>index 8fbef7a1a3a..81b4a75236d 100644
>--- a/libstdc++-v3/include/bits/stl_bvector.h
>+++ b/libstdc++-v3/include/bits/stl_bvector.h
>@@ -437,7 +437,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>
> struct _Bvector_impl_data
> {
>+#if !_GLIBCXX_INLINE_VERSION
> _Bit_iterator _M_start;
>+#else
>+ _Bit_type* _M_start;
>+#endif
An alternative that would require fewer changes elsewhere in the file
would be:
#if !_GLIBCXX_INLINE_VERSION
_Bit_iterator _M_start;
#else
// We don't need the offset field for the start pointer,
// it's always zero.
struct {
_Bit_type* _M_p;
// Allow assignment from iterators (assume offset is zero):
void operator=(_Bit_iterator __it) { _M_p = __it._M_p; }
} _M_start;
#endif
Now the rest of the file doesn't need any checks for
_GLIBCXX_INLINE_VERSION (which I'd prefer to avoid, because it
clutters the code up with extra conditionals for a mode that
**nobody** uses in practice).
> _Bit_iterator _M_finish;
> _Bit_pointer _M_end_of_storage;
>
>@@ -447,32 +451,74 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>
> #if __cplusplus >= 201103L
> _Bvector_impl_data(_Bvector_impl_data&& __x) noexcept
>- : _M_start(__x._M_start), _M_finish(__x._M_finish)
>- , _M_end_of_storage(__x._M_end_of_storage)
>+ : _Bvector_impl_data(__x)
> { __x._M_reset(); }
>
>+ _Bvector_impl_data(const _Bvector_impl_data&) = default;
>+ _Bvector_impl_data&
>+ operator=(const _Bvector_impl_data&) = default;
> void
> _M_move_data(_Bvector_impl_data&& __x) noexcept
> {
>- this->_M_start = __x._M_start;
>- this->_M_finish = __x._M_finish;
>- this->_M_end_of_storage = __x._M_end_of_storage;
>+ *this = __x;
> __x._M_reset();
> }
>+#else
>+ _Bvector_impl_data(const _Bvector_impl_data& __x)
>+ : _M_start(__x._M_start), _M_finish(__x._M_finish)
>+ , _M_end_of_storage(__x._M_end_of_storage)
>+ { }
Do we need this definition? Won't the compiler generate the same thing
for us anyway?
>+ _Bvector_impl_data&
>+ operator=(const _Bvector_impl_data& __x)
>+ {
>+ _M_start = __x._M_start;
>+ _M_finish = __x._M_finish;
>+ _M_end_of_storage = __x._M_end_of_storage;
>+ }
Same here.
>+#endif
>+
>+ _Bit_iterator
>+ _M_begin() const _GLIBCXX_NOEXCEPT
>+ { return _Bit_iterator(_M_start_p(), 0); }
>+
>+ _Bit_const_iterator
>+ _M_cbegin() const _GLIBCXX_NOEXCEPT
>+ { return _Bit_const_iterator(_M_start_p(), 0); }
>+
>+ _Bit_type*
>+ _M_start_p() const _GLIBCXX_NOEXCEPT
We already have _M_end_addr() so would _M_begin_addr be a better name
for this new function?
>+#if !_GLIBCXX_INLINE_VERSION
>+ { return _M_start._M_p; }
>+#else
>+ { return _M_start; }
>+#endif
Using the suggestion above you wouldn't need #if because return
_M_start._M_p would always be right. But you wouldn't even need this
function at all, the code that uses _M_start._M_p would Just Workâ¢.
>+ void
>+ _M_set_start(_Bit_type* __p) _GLIBCXX_NOEXCEPT
>+#if !_GLIBCXX_INLINE_VERSION
>+ { _M_start._M_p = __p; }
>+#else
>+ { _M_start = __p; }
> #endif
You wouldn't need this function.
etc. etc.
>
> void
> _M_reset() _GLIBCXX_NOEXCEPT
>+ { *this = _Bvector_impl_data(); }
>+
>+ void
>+ _M_swap_data(_Bvector_impl_data& __x) _GLIBCXX_NOEXCEPT
> {
>- _M_start = _M_finish = _Bit_iterator();
>- _M_end_of_storage = _Bit_pointer();
>+ // Do not use std::swap(_M_start, __x._M_start), etc as it loses
>+ // information used by TBAA.
>+ std::swap(*this, __x);
> }
> };
>
> struct _Bvector_impl
> : public _Bit_alloc_type, public _Bvector_impl_data
> {
>- public:
> _Bvector_impl()
> _GLIBCXX_NOEXCEPT_IF( noexcept(_Bit_alloc_type()) )
You'll need to rebase the patch, because I just fixed a bug in this
exception specification (r265626).
> : _Bit_alloc_type()
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: std::vector<bool> fix & enhancements
2018-10-31 2:28 ` Jonathan Wakely
@ 2018-10-31 17:58 ` François Dumont
0 siblings, 0 replies; 4+ messages in thread
From: François Dumont @ 2018-10-31 17:58 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: libstdc++, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 10486 bytes --]
Here is the updated patch.
I forgot that pre-C+11 we already had the default copy constructor and
assignment operators. And with the trick on definition of _M_start the
patch is much smaller.
   * include/bits/stl_bvector.h
   [_GLIBCXX_INLINE_VERSION](_Bvector_impl_data::_M_start): Define as
   _Bit_type*.
   (_Bvector_impl_data(const _Bvector_impl_data&)): Default.
   (_Bvector_impl_data(_Bvector_impl_data&&)): Delegate to latter.
   (_Bvector_impl_data::operator=(const _Bvector_impl_data&)): Default.
(_Bvector_impl_data::_M_move_data(_Bvector_impl_data&&)): Use latter.
   (_Bvector_impl_data::_M_reset()): Likewise.
   (_Bvector_impl_data::_M_swap_data): New.
   (_Bvector_impl::_Bvector_impl(_Bvector_impl&&)): Implement explicitely.
   (_Bvector_impl::_Bvector_impl(_Bit_alloc_type&&, _Bvector_impl&&)):
New.
   (_Bvector_base::_Bvector_base(_Bvector_base&&, const allocator_type&)):
   New, use latter.
   (vector::vector(vector&&, const allocator_type&, true_type)): New, use
   latter.
   (vector::vector(vector&&, const allocator_type&, false_type)): New.
   (vector::vector(vector&&, const allocator_type&)): Use latters.
   (vector::vector(const vector&, const allocator_type&)): Adapt.
   (vector::operator[](size_type)): Use iterator operator[].
   (vector::operator[](size_type) const): Use const_iterator operator[].
   (vector::swap(vector&)): Adapt.
   (vector::_M_initialize(size_type)): Adapt.
   * testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc:
   Add check.
Tested under Linux x86_64 w/o versioned namespace, w/o C++98 mode.
Ok to commit ?
François
On 10/30/18 10:59 PM, Jonathan Wakely wrote:
> On 30/10/18 07:28 +0100, François Dumont wrote:
>> Following Marc Glisse change to ignore _M_start offset I wanted to go
>> a little step further and just remove it in _GLIBCXX_INLINE_VERSION
>> mode.
>>
>> I also fix a regression we already fixed on mainstream std::vector
>> regarding noexcept qualification of move constructor with allocator.
>>
>> And I implemented the same optimizations than in std::vector for
>> allocators always comparing equals and for the std::swap operation.
>>
>> I also avoid re-implementing in vector::operator[] the same code
>> already implemented in iterator::operator[] but this one should
>> perhaps go in a different commit.
>>
>>
>> Â Â Â * include/bits/stl_bvector.h
>> Â Â Â [_GLIBCXX_INLINE_VERSION](_Bvector_impl_data::_M_start): Define as
>> Â Â Â _Bit_type*.
>> Â Â Â (_Bvector_impl_data(const _Bvector_impl_data&)): New.
>> Â Â Â (_Bvector_impl_data(_Bvector_impl_data&&)): Delegate to latter.
>> Â Â Â (_Bvector_impl_data::operator=(const _Bvector_impl_data&)): New.
>> (_Bvector_impl_data::_M_move_data(_Bvector_impl_data&&)): Use latter.
>> Â Â Â (_Bvector_impl_data::_M_reset()): Likewise.
>> Â Â Â (_Bvector_impl_data::_M_begin()): New.
>> Â Â Â (_Bvector_impl_data::_M_cbegin()): New.
>> Â Â Â (_Bvector_impl_data::_M_start_p()): New.
>> Â Â Â (_Bvector_impl_data::_M_set_start(_Bit_type*)): New.
>> Â Â Â (_Bvector_impl_data::_M_swap_data): New.
>> Â Â Â (_Bvector_impl::_Bvector_impl(_Bvector_impl&&)): Implement
>> explicitely.
>> Â Â Â (_Bvector_impl::_Bvector_impl(_Bit_alloc_type&&,
>> _Bvector_impl&&)): New.
>> Â Â Â (_Bvector_base::_Bvector_base(_Bvector_base&&, const
>> allocator_type&)):
>> Â Â Â New.
>> Â Â Â (_Bvector_base::_M_deallocate()): Adapt.
>> Â Â Â (vector::vector(const vector&, const allocator_type&)): Adapt.
>> Â Â Â (vector::vector(vector&&, const allocator_type&, true_type)): New.
>> Â Â Â (vector::vector(vector&&, const allocator_type&, false_type)): New.
>> Â Â Â (vector::vector(vector&&, const allocator_type&)): Use latters.
>> Â Â Â (vector::vector(const vector&, const allocator_type&)): Adapt.
>> Â Â Â (vector::begin()): Adapt.
>> Â Â Â (vector::cbegin()): Adapt.
>> Â Â Â (vector::operator[](size_type)): Use iterator operator[].
>> Â Â Â (vector::swap(vector&)): Adapt.
>> Â Â Â (vector::flip()): Adapt.
>> Â Â Â (vector::_M_initialize(size_type)): Adapt.
>> Â Â Â (vector::_M_initialize_value(bool)): Adapt.
>> Â Â Â * include/bits/vector.tcc:
>> Â Â Â (vector<bool>::_M_reallocate(size_type)): Adapt.
>> Â Â Â (vector<bool>::_M_fill_insert(iterator, size_type, bool)): Adapt.
>> (vector<bool>::_M_insert_range<_FwdIter>(iterator, _FwdIter, _FwdIter
>> Â Â Â std::forward_iterator_tag)): Adapt.
>> Â Â Â (vector<bool>::_M_insert_aux(iterator, bool)): Adapt.
>> Â Â Â (std::hash<std::vector<bool>>::operator()): Adapt.
>> Â Â Â *
>> testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc:
>> Â Â Â Add check.
>>
>> Tested under Linux x86_64.
>>
>> Ok to commit ?
>>
>> François
>>
>
>> diff --git a/libstdc++-v3/include/bits/stl_bvector.h
>> b/libstdc++-v3/include/bits/stl_bvector.h
>> index 8fbef7a1a3a..81b4a75236d 100644
>> --- a/libstdc++-v3/include/bits/stl_bvector.h
>> +++ b/libstdc++-v3/include/bits/stl_bvector.h
>> @@ -437,7 +437,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>
>> Â Â Â Â Â struct _Bvector_impl_data
>> Â Â Â Â Â {
>> +#if !_GLIBCXX_INLINE_VERSION
>>     _Bit_iterator   _M_start;
>> +#else
>> +Â Â Â _Bit_type*Â Â Â _M_start;
>> +#endif
>
> An alternative that would require fewer changes elsewhere in the file
> would be:
>
> #if !_GLIBCXX_INLINE_VERSION
>     _Bit_iterator   _M_start;
> #else
> Â Â Â Â Â Â // We don't need the offset field for the start pointer,
> Â Â Â Â Â Â // it's always zero.
> Â Â Â Â Â Â struct {
> Â Â Â Â Â Â Â Â _Bit_type* _M_p;
> Â Â Â Â Â Â Â Â // Allow assignment from iterators (assume offset is zero):
> Â Â Â Â Â Â Â Â void operator=(_Bit_iterator __it) { _M_p = __it._M_p; }
> Â Â Â Â Â Â } _M_start;
> #endif
>
> Now the rest of the file doesn't need any checks for
> _GLIBCXX_INLINE_VERSION (which I'd prefer to avoid, because it
> clutters the code up with extra conditionals for a mode that
> **nobody** uses in practice).
>
>
>>     _Bit_iterator   _M_finish;
>>     _Bit_pointer   _M_end_of_storage;
>>
>> @@ -447,32 +451,74 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>
>> #if __cplusplus >= 201103L
>> Â Â Â Â _Bvector_impl_data(_Bvector_impl_data&& __x) noexcept
>> -Â Â Â : _M_start(__x._M_start), _M_finish(__x._M_finish)
>> -Â Â Â , _M_end_of_storage(__x._M_end_of_storage)
>> +Â Â Â : _Bvector_impl_data(__x)
>> Â Â Â Â { __x._M_reset(); }
>>
>> +Â Â Â _Bvector_impl_data(const _Bvector_impl_data&) = default;
>> +Â Â Â _Bvector_impl_data&
>> +Â Â Â operator=(const _Bvector_impl_data&) = default;
>> Â Â Â Â void
>> Â Â Â Â _M_move_data(_Bvector_impl_data&& __x) noexcept
>> Â Â Â Â {
>> -Â Â Â Â Â this->_M_start = __x._M_start;
>> -Â Â Â Â Â this->_M_finish = __x._M_finish;
>> -Â Â Â Â Â this->_M_end_of_storage = __x._M_end_of_storage;
>> +Â Â Â Â Â *this = __x;
>> Â Â Â Â Â __x._M_reset();
>> Â Â Â Â }
>> +#else
>> +Â Â Â _Bvector_impl_data(const _Bvector_impl_data& __x)
>> +Â Â Â Â Â : _M_start(__x._M_start), _M_finish(__x._M_finish)
>> +Â Â Â Â Â , _M_end_of_storage(__x._M_end_of_storage)
>> +Â Â Â { }
>
> Do we need this definition? Won't the compiler generate the same thing
> for us anyway?
>
>> +Â Â Â _Bvector_impl_data&
>> +Â Â Â operator=(const _Bvector_impl_data& __x)
>> +Â Â Â {
>> +Â Â Â Â Â _M_start = __x._M_start;
>> +Â Â Â Â Â _M_finish = __x._M_finish;
>> +Â Â Â Â Â _M_end_of_storage = __x._M_end_of_storage;
>> +Â Â Â }
>
> Same here.
>
>> +#endif
>> +
>> +Â Â Â _Bit_iterator
>> +Â Â Â _M_begin() const _GLIBCXX_NOEXCEPT
>> +Â Â Â { return _Bit_iterator(_M_start_p(), 0); }
>> +
>> +Â Â Â _Bit_const_iterator
>> +Â Â Â _M_cbegin() const _GLIBCXX_NOEXCEPT
>> +Â Â Â { return _Bit_const_iterator(_M_start_p(), 0); }
>> +
>> +Â Â Â _Bit_type*
>> +Â Â Â _M_start_p() const _GLIBCXX_NOEXCEPT
>
> We already have _M_end_addr() so would _M_begin_addr be a better name
> for this new function?
>
>> +#if !_GLIBCXX_INLINE_VERSION
>> +Â Â Â { return _M_start._M_p; }
>> +#else
>> +Â Â Â { return _M_start; }
>> +#endif
>
> Using the suggestion above you wouldn't need #if because return
> _M_start._M_p would always be right. But you wouldn't even need this
> function at all, the code that uses _M_start._M_p would Just Workâ¢.
>
>
>> +Â Â Â void
>> +Â Â Â _M_set_start(_Bit_type* __p) _GLIBCXX_NOEXCEPT
>> +#if !_GLIBCXX_INLINE_VERSION
>> +Â Â Â { _M_start._M_p = __p; }
>> +#else
>> +Â Â Â { _M_start = __p; }
>> #endif
>
> You wouldn't need this function.
>
> etc. etc.
>
>
>>
>> Â Â Â Â void
>> Â Â Â Â _M_reset() _GLIBCXX_NOEXCEPT
>> +Â Â Â { *this = _Bvector_impl_data(); }
>> +
>> +Â Â Â void
>> +Â Â Â _M_swap_data(_Bvector_impl_data& __x) _GLIBCXX_NOEXCEPT
>> Â Â Â Â {
>> -Â Â Â Â Â _M_start = _M_finish = _Bit_iterator();
>> -Â Â Â Â Â _M_end_of_storage = _Bit_pointer();
>> +Â Â Â Â Â // Do not use std::swap(_M_start, __x._M_start), etc as it loses
>> +Â Â Â Â Â // information used by TBAA.
>> +Â Â Â Â Â std::swap(*this, __x);
>> Â Â Â Â }
>> Â Â Â Â Â };
>>
>> Â Â Â Â Â struct _Bvector_impl
>> Â Â Â Â : public _Bit_alloc_type, public _Bvector_impl_data
>> Â Â Â Â Â {
>> -Â Â Â public:
>> Â Â Â Â _Bvector_impl()
>> Â Â Â Â Â _GLIBCXX_NOEXCEPT_IF( noexcept(_Bit_alloc_type()) )
>
> You'll need to rebase the patch, because I just fixed a bug in this
> exception specification (r265626).
>
>> Â Â Â Â : _Bit_alloc_type()
>
[-- Attachment #2: bvector.patch --]
[-- Type: text/x-patch, Size: 7895 bytes --]
diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h
index 0bcfd19fd3e..387f71b8b62 100644
--- a/libstdc++-v3/include/bits/stl_bvector.h
+++ b/libstdc++-v3/include/bits/stl_bvector.h
@@ -437,7 +437,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
struct _Bvector_impl_data
{
+#if !_GLIBCXX_INLINE_VERSION
_Bit_iterator _M_start;
+#else
+ // We don't need the offset field for the start pointer,
+ // it's always zero.
+ struct {
+ _Bit_type* _M_p;
+ // Allow assignment from iterators (assume offset is zero):
+ void operator=(_Bit_iterator __it) { _M_p = __it._M_p; }
+ } _M_start;
+#endif
_Bit_iterator _M_finish;
_Bit_pointer _M_end_of_storage;
@@ -446,33 +456,38 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{ }
#if __cplusplus >= 201103L
+ _Bvector_impl_data(const _Bvector_impl_data&) = default;
+ _Bvector_impl_data&
+ operator=(const _Bvector_impl_data&) = default;
+
_Bvector_impl_data(_Bvector_impl_data&& __x) noexcept
- : _M_start(__x._M_start), _M_finish(__x._M_finish)
- , _M_end_of_storage(__x._M_end_of_storage)
+ : _Bvector_impl_data(__x)
{ __x._M_reset(); }
void
_M_move_data(_Bvector_impl_data&& __x) noexcept
{
- this->_M_start = __x._M_start;
- this->_M_finish = __x._M_finish;
- this->_M_end_of_storage = __x._M_end_of_storage;
+ *this = __x;
__x._M_reset();
}
#endif
void
_M_reset() _GLIBCXX_NOEXCEPT
+ { *this = _Bvector_impl_data(); }
+
+ void
+ _M_swap_data(_Bvector_impl_data& __x) _GLIBCXX_NOEXCEPT
{
- _M_start = _M_finish = _Bit_iterator();
- _M_end_of_storage = _Bit_pointer();
+ // Do not use std::swap(_M_start, __x._M_start), etc as it loses
+ // information used by TBAA.
+ std::swap(*this, __x);
}
};
struct _Bvector_impl
: public _Bit_alloc_type, public _Bvector_impl_data
{
- public:
_Bvector_impl() _GLIBCXX_NOEXCEPT_IF(
is_nothrow_default_constructible<_Bit_alloc_type>::value)
: _Bit_alloc_type()
@@ -483,7 +498,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{ }
#if __cplusplus >= 201103L
- _Bvector_impl(_Bvector_impl&&) = default;
+ // Not defaulted, to enforce noexcept(true) even when
+ // !is_nothrow_move_constructible<_Bit_alloc_type>.
+ _Bvector_impl(_Bvector_impl&& __x) noexcept
+ : _Bit_alloc_type(std::move(__x)), _Bvector_impl_data(std::move(__x))
+ { }
+
+ _Bvector_impl(_Bit_alloc_type&& __a, _Bvector_impl&& __x) noexcept
+ : _Bit_alloc_type(std::move(__a)), _Bvector_impl_data(std::move(__x))
+ { }
#endif
_Bit_type*
@@ -521,6 +544,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#if __cplusplus >= 201103L
_Bvector_base(_Bvector_base&&) = default;
+
+ _Bvector_base(_Bvector_base&& __x, const allocator_type& __a)
+ : _M_impl(_Bit_alloc_type(__a), std::move(__x._M_impl))
+ { }
#endif
~_Bvector_base()
@@ -657,14 +684,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
: _Base(_Bit_alloc_traits::_S_select_on_copy(__x._M_get_Bit_allocator()))
{
_M_initialize(__x.size());
- _M_copy_aligned(__x.begin(), __x.end(), this->_M_impl._M_start);
+ _M_copy_aligned(__x.begin(), __x.end(), begin());
}
#if __cplusplus >= 201103L
vector(vector&&) = default;
- vector(vector&& __x, const allocator_type& __a)
- noexcept(_Bit_alloc_traits::_S_always_equal())
+ private:
+ vector(vector&& __x, const allocator_type& __a, true_type) noexcept
+ : _Base(std::move(__x), __a)
+ { }
+
+ vector(vector&& __x, const allocator_type& __a, false_type)
: _Base(__a)
{
if (__x.get_allocator() == __a)
@@ -677,11 +708,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
}
+ public:
+ vector(vector&& __x, const allocator_type& __a)
+ noexcept(_Bit_alloc_traits::_S_always_equal())
+ : vector(std::move(__x), __a,
+ typename _Bit_alloc_traits::is_always_equal{})
+ { }
+
vector(const vector& __x, const allocator_type& __a)
: _Base(__a)
{
_M_initialize(__x.size());
- _M_copy_aligned(__x.begin(), __x.end(), this->_M_impl._M_start);
+ _M_copy_aligned(__x.begin(), __x.end(), begin());
}
vector(initializer_list<bool> __l,
@@ -884,17 +922,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
reference
operator[](size_type __n)
- {
- return *iterator(this->_M_impl._M_start._M_p
- + __n / int(_S_word_bit), __n % int(_S_word_bit));
- }
+ { return begin()[__n]; }
const_reference
operator[](size_type __n) const
- {
- return *const_iterator(this->_M_impl._M_start._M_p
- + __n / int(_S_word_bit), __n % int(_S_word_bit));
- }
+ { return begin()[__n]; }
protected:
void
@@ -961,10 +993,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
void
swap(vector& __x) _GLIBCXX_NOEXCEPT
{
- std::swap(this->_M_impl._M_start, __x._M_impl._M_start);
- std::swap(this->_M_impl._M_finish, __x._M_impl._M_finish);
- std::swap(this->_M_impl._M_end_of_storage,
- __x._M_impl._M_end_of_storage);
+#if __cplusplus >= 201103L
+ __glibcxx_assert(_Bit_alloc_traits::propagate_on_container_swap::value
+ || _M_get_Bit_allocator() == __x._M_get_Bit_allocator());
+#endif
+ this->_M_impl._M_swap_data(__x._M_impl);
_Bit_alloc_traits::_S_on_swap(_M_get_Bit_allocator(),
__x._M_get_Bit_allocator());
}
@@ -1123,15 +1156,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{
_Bit_pointer __q = this->_M_allocate(__n);
this->_M_impl._M_end_of_storage = __q + _S_nword(__n);
- this->_M_impl._M_start = iterator(std::__addressof(*__q), 0);
+ iterator __start = iterator(std::__addressof(*__q), 0);
+ this->_M_impl._M_start = __start;
+ this->_M_impl._M_finish = __start + difference_type(__n);
}
else
- {
- this->_M_impl._M_end_of_storage = _Bit_pointer();
- this->_M_impl._M_start = iterator(0, 0);
- }
- this->_M_impl._M_finish = this->_M_impl._M_start + difference_type(__n);
-
+ this->_M_impl._M_reset();
}
void
@@ -1186,7 +1216,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{
const size_type __n = std::distance(__first, __last);
_M_initialize(__n);
- std::copy(__first, __last, this->_M_impl._M_start);
+ std::copy(__first, __last, begin());
}
#if __cplusplus < 201103L
diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc
index 62429424a5f..fa2b053c66e 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/bool/cons/noexcept_move_construct.cc
@@ -23,4 +23,34 @@
typedef std::vector<bool> vbtype;
-static_assert(std::is_nothrow_move_constructible<vbtype>::value, "Error");
+static_assert( std::is_nothrow_move_constructible<vbtype>::value,
+ "noexcept move constructor" );
+static_assert( std::is_nothrow_constructible<vbtype,
+ vbtype&&, const typename vbtype::allocator_type&>::value,
+ "noexcept move constructor with allocator" );
+
+template<typename Type>
+ class not_noexcept_move_constructor_alloc : public std::allocator<Type>
+ {
+ public:
+ not_noexcept_move_constructor_alloc() noexcept { }
+
+ not_noexcept_move_constructor_alloc(
+ const not_noexcept_move_constructor_alloc& x) noexcept
+ : std::allocator<Type>(x)
+ { }
+
+ not_noexcept_move_constructor_alloc(
+ not_noexcept_move_constructor_alloc&& x) noexcept(false)
+ : std::allocator<Type>(std::move(x))
+ { }
+
+ template<typename _Tp1>
+ struct rebind
+ { typedef not_noexcept_move_constructor_alloc<_Tp1> other; };
+ };
+
+typedef std::vector<bool, not_noexcept_move_constructor_alloc<bool>> vbtype2;
+
+static_assert( std::is_nothrow_move_constructible<vbtype2>::value,
+ "noexcept move constructor with not noexcept alloc" );
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2018-10-31 17:35 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-30 9:12 std::vector<bool> fix & enhancements François Dumont
2018-10-31 1:42 ` François Dumont
2018-10-31 2:28 ` Jonathan Wakely
2018-10-31 17:58 ` François Dumont
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).