Skip to content

Commit 45a3109

Browse files
committed
Recommit r370502: Make vector unconditionally move elements when
exceptions are disabled. The patch was reverted due to some confusion about non-movable types. ie types that explicitly delete their move constructors. However, such types do not meet the requirement for `MoveConstructible`, which is required by `std::vector`: Summary: `std::vector<T>` is free choose between using copy or move operations when it needs to resize. The standard only candidates that the correct exception safety guarantees are provided. When exceptions are disabled these guarantees are trivially satisfied. Meaning vector is free to optimize it's implementation by moving instead of copying. This patch makes `std::vector` unconditionally move elements when exceptions are disabled. This optimization is conforming according to the current standard wording. There are concerns that moving in `-fno-noexceptions`mode will be a surprise to users. For example, a user may be surprised to find their code is slower with exceptions enabled than it is disabled. I'm sympathetic to this surprised, but I don't think it should block this optimization. Reviewers: mclow.lists, ldionne, rsmith Reviewed By: ldionne Subscribers: zoecarver, christof, dexonsmith, libcxx-commits Tags: #libc Differential Revision: https://reviews.llvm.org/D62228 git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@371867 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent dbc7f4e commit 45a3109

File tree

5 files changed

+157
-55
lines changed

5 files changed

+157
-55
lines changed

include/memory

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,6 +1507,31 @@ struct __is_default_allocator : false_type {};
15071507
template <class _Tp>
15081508
struct __is_default_allocator<_VSTD::allocator<_Tp> > : true_type {};
15091509

1510+
1511+
1512+
template <class _Alloc,
1513+
bool = __has_construct<_Alloc, typename _Alloc::value_type*, typename _Alloc::value_type&&>::value && !__is_default_allocator<_Alloc>::value
1514+
>
1515+
struct __is_cpp17_move_insertable;
1516+
template <class _Alloc>
1517+
struct __is_cpp17_move_insertable<_Alloc, true> : std::true_type {};
1518+
template <class _Alloc>
1519+
struct __is_cpp17_move_insertable<_Alloc, false> : std::is_move_constructible<typename _Alloc::value_type> {};
1520+
1521+
template <class _Alloc,
1522+
bool = __has_construct<_Alloc, typename _Alloc::value_type*, const typename _Alloc::value_type&>::value && !__is_default_allocator<_Alloc>::value
1523+
>
1524+
struct __is_cpp17_copy_insertable;
1525+
template <class _Alloc>
1526+
struct __is_cpp17_copy_insertable<_Alloc, true> : __is_cpp17_move_insertable<_Alloc> {};
1527+
template <class _Alloc>
1528+
struct __is_cpp17_copy_insertable<_Alloc, false> : integral_constant<bool,
1529+
std::is_copy_constructible<typename _Alloc::value_type>::value &&
1530+
__is_cpp17_move_insertable<_Alloc>::value>
1531+
{};
1532+
1533+
1534+
15101535
template <class _Alloc>
15111536
struct _LIBCPP_TEMPLATE_VIS allocator_traits
15121537
{
@@ -1609,10 +1634,18 @@ struct _LIBCPP_TEMPLATE_VIS allocator_traits
16091634
_LIBCPP_INLINE_VISIBILITY
16101635
static
16111636
void
1612-
__construct_forward(allocator_type& __a, _Ptr __begin1, _Ptr __end1, _Ptr& __begin2)
1637+
__construct_forward_with_exception_guarantees(allocator_type& __a, _Ptr __begin1, _Ptr __end1, _Ptr& __begin2)
16131638
{
1639+
static_assert(__is_cpp17_move_insertable<allocator_type>::value,
1640+
"The specified type does not meet the requirements of Cpp17MoveInsertible");
16141641
for (; __begin1 != __end1; ++__begin1, (void) ++__begin2)
1615-
construct(__a, _VSTD::__to_raw_pointer(__begin2), _VSTD::move_if_noexcept(*__begin1));
1642+
construct(__a, _VSTD::__to_raw_pointer(__begin2),
1643+
#ifdef _LIBCPP_NO_EXCEPTIONS
1644+
_VSTD::move(*__begin1)
1645+
#else
1646+
_VSTD::move_if_noexcept(*__begin1)
1647+
#endif
1648+
);
16161649
}
16171650

16181651
template <class _Tp>
@@ -1625,7 +1658,7 @@ struct _LIBCPP_TEMPLATE_VIS allocator_traits
16251658
is_trivially_move_constructible<_Tp>::value,
16261659
void
16271660
>::type
1628-
__construct_forward(allocator_type&, _Tp* __begin1, _Tp* __end1, _Tp*& __begin2)
1661+
__construct_forward_with_exception_guarantees(allocator_type&, _Tp* __begin1, _Tp* __end1, _Tp*& __begin2)
16291662
{
16301663
ptrdiff_t _Np = __end1 - __begin1;
16311664
if (_Np > 0)
@@ -1672,12 +1705,20 @@ struct _LIBCPP_TEMPLATE_VIS allocator_traits
16721705
_LIBCPP_INLINE_VISIBILITY
16731706
static
16741707
void
1675-
__construct_backward(allocator_type& __a, _Ptr __begin1, _Ptr __end1, _Ptr& __end2)
1708+
__construct_backward_with_exception_guarantees(allocator_type& __a, _Ptr __begin1, _Ptr __end1, _Ptr& __end2)
16761709
{
1710+
static_assert(__is_cpp17_move_insertable<allocator_type>::value,
1711+
"The specified type does not meet the requirements of Cpp17MoveInsertable");
16771712
while (__end1 != __begin1)
16781713
{
1679-
construct(__a, _VSTD::__to_raw_pointer(__end2-1), _VSTD::move_if_noexcept(*--__end1));
1680-
--__end2;
1714+
construct(__a, _VSTD::__to_raw_pointer(__end2 - 1),
1715+
#ifdef _LIBCPP_NO_EXCEPTIONS
1716+
_VSTD::move(*--__end1)
1717+
#else
1718+
_VSTD::move_if_noexcept(*--__end1)
1719+
#endif
1720+
);
1721+
--__end2;
16811722
}
16821723
}
16831724

@@ -1691,7 +1732,7 @@ struct _LIBCPP_TEMPLATE_VIS allocator_traits
16911732
is_trivially_move_constructible<_Tp>::value,
16921733
void
16931734
>::type
1694-
__construct_backward(allocator_type&, _Tp* __begin1, _Tp* __end1, _Tp*& __end2)
1735+
__construct_backward_with_exception_guarantees(allocator_type&, _Tp* __begin1, _Tp* __end1, _Tp*& __end2)
16951736
{
16961737
ptrdiff_t _Np = __end1 - __begin1;
16971738
__end2 -= _Np;

include/vector

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -947,8 +947,10 @@ template <class _Tp, class _Allocator>
947947
void
948948
vector<_Tp, _Allocator>::__swap_out_circular_buffer(__split_buffer<value_type, allocator_type&>& __v)
949949
{
950+
950951
__annotate_delete();
951-
__alloc_traits::__construct_backward(this->__alloc(), this->__begin_, this->__end_, __v.__begin_);
952+
__alloc_traits::__construct_backward_with_exception_guarantees(
953+
this->__alloc(), this->__begin_, this->__end_, __v.__begin_);
952954
_VSTD::swap(this->__begin_, __v.__begin_);
953955
_VSTD::swap(this->__end_, __v.__end_);
954956
_VSTD::swap(this->__end_cap(), __v.__end_cap());
@@ -963,8 +965,10 @@ vector<_Tp, _Allocator>::__swap_out_circular_buffer(__split_buffer<value_type, a
963965
{
964966
__annotate_delete();
965967
pointer __r = __v.__begin_;
966-
__alloc_traits::__construct_backward(this->__alloc(), this->__begin_, __p, __v.__begin_);
967-
__alloc_traits::__construct_forward(this->__alloc(), __p, this->__end_, __v.__end_);
968+
__alloc_traits::__construct_backward_with_exception_guarantees(
969+
this->__alloc(), this->__begin_, __p, __v.__begin_);
970+
__alloc_traits::__construct_forward_with_exception_guarantees(
971+
this->__alloc(), __p, this->__end_, __v.__end_);
968972
_VSTD::swap(this->__begin_, __v.__begin_);
969973
_VSTD::swap(this->__end_, __v.__end_);
970974
_VSTD::swap(this->__end_cap(), __v.__end_cap());
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// RUN: %build -fno-exceptions
10+
// RUN: %run
11+
12+
// UNSUPPORTED: c++98, c++03
13+
14+
// <vector>
15+
16+
// Test that vector always moves elements when exceptions are disabled.
17+
// vector is allowed to move or copy elements while resizing, so long as
18+
// it still provides the strong exception safety guarantee.
19+
20+
#include <vector>
21+
#include <cassert>
22+
23+
#include "test_macros.h"
24+
25+
#ifndef TEST_HAS_NO_EXCEPTIONS
26+
#error exceptions should be disabled.
27+
#endif
28+
29+
bool allow_moves = false;
30+
31+
class A {
32+
public:
33+
A() {}
34+
A(A&&) { assert(allow_moves); }
35+
explicit A(int) {}
36+
A(A const&) { assert(false); }
37+
};
38+
39+
int main(int, char**) {
40+
std::vector<A> v;
41+
42+
// Create a vector containing some number of elements that will
43+
// have to be moved when it is resized.
44+
v.reserve(10);
45+
size_t old_cap = v.capacity();
46+
for (int i = 0; i < v.capacity(); ++i) {
47+
v.emplace_back(42);
48+
}
49+
assert(v.capacity() == old_cap);
50+
assert(v.size() == v.capacity());
51+
52+
// The next emplace back should resize.
53+
allow_moves = true;
54+
v.emplace_back(42);
55+
56+
return 0;
57+
}

test/std/containers/sequences/vector/vector.modifiers/resize.copy_only.pass.sh.cpp

Lines changed: 0 additions & 45 deletions
This file was deleted.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++98, c++03
10+
11+
12+
// <vector>
13+
14+
// Test that vector produces a decent diagnostic for user types that explicitly
15+
// delete their move constructor. Such types don't meet the Cpp17CopyInsertible
16+
// requirements.
17+
18+
#include <vector>
19+
20+
template <int>
21+
class BadUserNoCookie {
22+
public:
23+
BadUserNoCookie() { }
24+
25+
BadUserNoCookie(BadUserNoCookie&&) = delete;
26+
BadUserNoCookie& operator=(BadUserNoCookie&&) = delete;
27+
28+
BadUserNoCookie(const BadUserNoCookie&) = default;
29+
BadUserNoCookie& operator=(const BadUserNoCookie&) = default;
30+
};
31+
32+
int main() {
33+
// expected-error@memory:* 2 {{"The specified type does not meet the requirements of Cpp17MoveInsertable"}}
34+
{
35+
36+
std::vector<BadUserNoCookie<1> > x;
37+
x.emplace_back();
38+
}
39+
{
40+
std::vector<BadUserNoCookie<2>> x;
41+
BadUserNoCookie<2> c;
42+
x.push_back(c);
43+
}
44+
return 0;
45+
}

0 commit comments

Comments
 (0)