2019-02-05 Jonathan Wakely <jwakely@redhat.com>
+ PR libstdc++/89130
+ * include/bits/alloc_traits.h (__is_copy_insertable_impl): Rename to
+ __is_alloc_insertable_impl. Replace single type member with two
+ members, one for each of copy and move insertable.
+ (__is_move_insertable): New trait for internal use.
+ * include/bits/stl_vector.h (vector::_S_nothrow_relocate(true_type))
+ (vector::_S_nothrow_relocate(true_type)): New functions to
+ conditionally check if __relocate_a can throw.
+ (vector::_S_use_relocate()): Dispatch to _S_nothrow_relocate based
+ on __is_move_insertable.
+ (vector::_S_do_relocate): New overloaded functions to conditionally
+ call __relocate_a.
+ (vector::_S_relocate): New function that dispatches to _S_do_relocate
+ based on _S_use_relocate.
+ * include/bits/vector.tcc (vector::reserve, vector::_M_realloc_insert)
+ (vector::_M_default_append): Call _S_relocate instead of __relocate_a.
+ * testsuite/23_containers/vector/modifiers/push_back/89130.cc: New.
+
PR libstdc++/89090
* include/bits/stl_uninitialized.h (__relocate_a_1): Make unused
parameter unnamed. Add message to static assertion.
}
template<typename _Alloc>
- class __is_copy_insertable_impl
+ class __is_alloc_insertable_impl
{
- typedef allocator_traits<_Alloc> _Traits;
+ using _Traits = allocator_traits<_Alloc>;
+ using value_type = typename _Traits::value_type;
- template<typename _Up, typename
+ template<typename _Up, typename _Tp = __remove_cvref_t<_Up>,
+ typename
= decltype(_Traits::construct(std::declval<_Alloc&>(),
- std::declval<_Up*>(),
- std::declval<const _Up&>()))>
+ std::declval<_Tp*>(),
+ std::declval<_Up>()))>
static true_type
_M_select(int);
_M_select(...);
public:
- typedef decltype(_M_select<typename _Alloc::value_type>(0)) type;
+ using copy = decltype(_M_select<const value_type&>(0));
+ using move = decltype(_M_select<value_type>(0));
};
// true if _Alloc::value_type is CopyInsertable into containers using _Alloc
template<typename _Alloc>
struct __is_copy_insertable
- : __is_copy_insertable_impl<_Alloc>::type
+ : __is_alloc_insertable_impl<_Alloc>::copy
{ };
// std::allocator<_Tp> just requires CopyConstructible
: is_copy_constructible<_Tp>
{ };
+ // true if _Alloc::value_type is MoveInsertable into containers using _Alloc
+ template<typename _Alloc>
+ struct __is_move_insertable
+ : __is_alloc_insertable_impl<_Alloc>::move
+ { };
+
+ // std::allocator<_Tp> just requires MoveConstructible
+ template<typename _Tp>
+ struct __is_move_insertable<allocator<_Tp>>
+ : is_move_constructible<_Tp>
+ { };
+
// Trait to detect Allocator-like types.
template<typename _Alloc, typename = void>
struct __is_allocator : false_type { };
private:
#if __cplusplus >= 201103L
static constexpr bool
- _S_use_relocate()
+ _S_nothrow_relocate(true_type)
{
return noexcept(std::__relocate_a(std::declval<pointer>(),
std::declval<pointer>(),
std::declval<pointer>(),
std::declval<_Tp_alloc_type&>()));
}
-#endif
+
+ static constexpr bool
+ _S_nothrow_relocate(false_type)
+ { return false; }
+
+ static constexpr bool
+ _S_use_relocate()
+ {
+ // Instantiating std::__relocate_a might cause an error outside the
+ // immediate context (in __relocate_object_a's noexcept-specifier),
+ // so only do it if we know the type can be move-inserted into *this.
+ return _S_nothrow_relocate(__is_move_insertable<_Tp_alloc_type>{});
+ }
+
+ static pointer
+ _S_do_relocate(pointer __first, pointer __last, pointer __result,
+ _Tp_alloc_type& __alloc, true_type) noexcept
+ {
+ return std::__relocate_a(__first, __last, __result, __alloc);
+ }
+
+ static pointer
+ _S_do_relocate(pointer, pointer, pointer __result,
+ _Tp_alloc_type&, false_type) noexcept
+ { return __result; }
+
+ static pointer
+ _S_relocate(pointer __first, pointer __last, pointer __result,
+ _Tp_alloc_type& __alloc) noexcept
+ {
+ using __do_it = __bool_constant<_S_use_relocate()>;
+ return _S_do_relocate(__first, __last, __result, __alloc, __do_it{});
+ }
+#endif // C++11
protected:
using _Base::_M_allocate;
if _GLIBCXX17_CONSTEXPR (_S_use_relocate())
{
__tmp = this->_M_allocate(__n);
- std::__relocate_a(this->_M_impl._M_start,
- this->_M_impl._M_finish,
- __tmp, _M_get_Tp_allocator());
+ _S_relocate(this->_M_impl._M_start, this->_M_impl._M_finish,
+ __tmp, _M_get_Tp_allocator());
}
else
#endif
#if __cplusplus >= 201103L
if _GLIBCXX17_CONSTEXPR (_S_use_relocate())
{
- __new_finish
- = std::__relocate_a
- (__old_start, __position.base(),
- __new_start, _M_get_Tp_allocator());
+ __new_finish = _S_relocate(__old_start, __position.base(),
+ __new_start, _M_get_Tp_allocator());
++__new_finish;
- __new_finish
- = std::__relocate_a
- (__position.base(), __old_finish,
- __new_finish, _M_get_Tp_allocator());
+ __new_finish = _S_relocate(__position.base(), __old_finish,
+ __new_finish, _M_get_Tp_allocator());
}
else
#endif
_M_deallocate(__new_start, __len);
__throw_exception_again;
}
- std::__relocate_a(this->_M_impl._M_start,
- this->_M_impl._M_finish,
- __new_start, _M_get_Tp_allocator());
+ _S_relocate(this->_M_impl._M_start, this->_M_impl._M_finish,
+ __new_start, _M_get_Tp_allocator());
}
else
{
--- /dev/null
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <vector>
+
+struct T
+{
+ T() { }
+ T(const T&) { }
+ T(T&&) = delete; // this means T is not MoveInsertable into std::vector<T>
+};
+
+void f()
+{
+ const T val;
+ std::vector<T> x;
+ // push_back(const T&) only requires T is CopyInsertable into std::vector<T>:
+ x.push_back(val);
+}
+
+template<typename U>
+struct Alloc
+{
+ using value_type = U;
+ Alloc() = default;
+ Alloc(const Alloc&) = default;
+ template<typename U2>
+ Alloc(const Alloc<U2>&) { }
+
+ U* allocate(unsigned n) { return std::allocator<U>().allocate(n); }
+ void deallocate(U* p, unsigned n) { std::allocator<U>().deallocate(p, n); }
+
+ void construct(Alloc*, U* p, U&& u)
+ {
+ // construct from const lvalue instead of rvalue:
+ ::new(p) U(const_cast<const U&>(u));
+ }
+};
+
+void g()
+{
+ const T val;
+ std::vector<T, Alloc<T>> x;
+ // push_back(const T&) only requires T is CopyInsertable into std::vector<T>:
+ x.push_back(val);
+}