Skip to content

Commit f2ef4fd

Browse files
committed
[libcxx] makes pair conditionally trivially copyable for C++20
`pair` can have trivial copy and move assignment operators when both its members have trivial copy and move operators, which opens `pair` up to being trivially copyable. This is the first of three possible commits to bring users a more robust implementation. It's currently unclear how to implement this without using a _requires-clause_, so the feature is restricted to C++20 for now. There has also been some interest in moving this out of the unstable ABI mode. Both of these changes are intended to arrive in future pull requests, should their feasibility pan out.
1 parent 725a052 commit f2ef4fd

File tree

6 files changed

+108
-1
lines changed

6 files changed

+108
-1
lines changed

libcxx/include/__config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@
180180
// requires code not to make these assumptions.
181181
# define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY
182182
# define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_STRING_VIEW
183+
// Allows std::pair's copy assignment and move assignment operators to be trivial
184+
// when both its element types' respective assignment operators are trivial.
185+
# define _LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE
183186
# elif _LIBCPP_ABI_VERSION == 1
184187
# if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
185188
// Enable compiling copies of now inline methods into the dylib to support

libcxx/include/__utility/pair.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <__compare/common_comparison_category.h>
1313
#include <__compare/synth_three_way.h>
14+
#include <__concepts/assignable.h>
1415
#include <__concepts/different_from.h>
1516
#include <__config>
1617
#include <__fwd/array.h>
@@ -26,6 +27,7 @@
2627
#include <__type_traits/common_type.h>
2728
#include <__type_traits/conditional.h>
2829
#include <__type_traits/decay.h>
30+
#include <__type_traits/enable_if.h>
2931
#include <__type_traits/integral_constant.h>
3032
#include <__type_traits/is_assignable.h>
3133
#include <__type_traits/is_constructible.h>
@@ -40,8 +42,11 @@
4042
#include <__type_traits/is_nothrow_copy_constructible.h>
4143
#include <__type_traits/is_nothrow_default_constructible.h>
4244
#include <__type_traits/is_nothrow_move_assignable.h>
45+
#include <__type_traits/is_object.h>
4346
#include <__type_traits/is_same.h>
4447
#include <__type_traits/is_swappable.h>
48+
#include <__type_traits/is_trivially_copy_assignable.h>
49+
#include <__type_traits/is_trivially_move_assignable.h>
4550
#include <__type_traits/nat.h>
4651
#include <__type_traits/remove_cvref.h>
4752
#include <__type_traits/unwrap_ref.h>
@@ -67,6 +72,14 @@ struct __non_trivially_copyable_base {
6772
__non_trivially_copyable_base(__non_trivially_copyable_base const&) _NOEXCEPT {}
6873
};
6974

75+
#if _LIBCPP_STD_VER >= 20
76+
template <class _Tp>
77+
concept __trivially_copy_assignable_object = is_trivially_copy_assignable_v<_Tp> && is_object_v<_Tp>;
78+
79+
template <class _Tp>
80+
concept __trivially_move_assignable_object = is_trivially_move_assignable_v<_Tp> && is_object_v<_Tp>;
81+
#endif
82+
7083
#if _LIBCPP_STD_VER >= 23
7184
template <class _Tp>
7285
struct __is_specialization_of_subrange : false_type {};
@@ -236,6 +249,28 @@ struct _LIBCPP_TEMPLATE_VIS pair
236249
typename __make_tuple_indices<sizeof...(_Args1)>::type(),
237250
typename __make_tuple_indices<sizeof...(_Args2) >::type()) {}
238251

252+
# if _LIBCPP_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
253+
_LIBCPP_HIDE_FROM_ABI pair& operator=(pair const& __p) = default;
254+
_LIBCPP_HIDE_FROM_ABI pair& operator=(pair&& __p) = default;
255+
256+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(pair const& __p)
257+
_NOEXCEPT_(is_nothrow_copy_assignable<first_type>::value&& is_nothrow_copy_assignable<second_type>::value)
258+
requires is_copy_assignable_v<first_type> && is_copy_assignable_v<second_type> && (!(__trivially_copy_assignable_object<first_type> && __trivially_copy_assignable_object<second_type>))
259+
{
260+
first = __p.first;
261+
second = __p.second;
262+
return *this;
263+
}
264+
265+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(pair&& __p)
266+
_NOEXCEPT_(is_nothrow_move_assignable<first_type>::value&& is_nothrow_move_assignable<second_type>::value)
267+
requires is_move_assignable_v<first_type> && is_move_assignable_v<second_type> && (!(__trivially_move_assignable_object<first_type> && __trivially_move_assignable_object<second_type>))
268+
{
269+
first = std::forward<first_type>(__p.first);
270+
second = std::forward<second_type>(__p.second);
271+
return *this;
272+
}
273+
# else
239274
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair&
240275
operator=(__conditional_t< is_copy_assignable<first_type>::value && is_copy_assignable<second_type>::value,
241276
pair,
@@ -254,6 +289,7 @@ struct _LIBCPP_TEMPLATE_VIS pair
254289
second = std::forward<second_type>(__p.second);
255290
return *this;
256291
}
292+
# endif
257293

258294
template <
259295
class _U1,

libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair.pass.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ TEST_CONSTEXPR_CXX20 bool test() {
8888
assert(&p.second == &inc_obj);
8989
}
9090

91+
#if TEST_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
92+
static_assert(std::is_trivially_copy_assignable<std::pair<int, int>>::value, "");
93+
#else
94+
static_assert(!std::is_trivially_copy_assignable<std::pair<int, int>>::value, "");
95+
#endif
96+
97+
static_assert(!std::is_trivially_copy_assignable<std::pair<CountAssign, int>>::value, "");
98+
static_assert(!std::is_trivially_copy_assignable<std::pair<int, CountAssign>>::value, "");
99+
static_assert(!std::is_trivially_copy_assignable<std::pair<CountAssign, CountAssign>>::value, "");
100+
91101
return true;
92102
}
93103

libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair_cxx03.pass.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ int main(int, char**)
6262
assert(p.second == 'x');
6363
}
6464

65-
return 0;
65+
static_assert(!std::is_trivially_copy_assignable<std::pair<int, int> >::value, "");
66+
67+
return 0;
6668
}
6769

6870
struct Incomplete {};

libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_rv_pair.pass.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,17 @@ TEST_CONSTEXPR_CXX20 bool test() {
130130
static_assert(!std::is_move_assignable<P5>::value, "");
131131
static_assert(!std::is_move_assignable<P6>::value, "");
132132
}
133+
134+
#if TEST_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
135+
static_assert(std::is_trivially_move_assignable<std::pair<int, int>>::value, "");
136+
#else
137+
static_assert(!std::is_trivially_move_assignable<std::pair<int, int>>::value, "");
138+
#endif
139+
140+
static_assert(!std::is_trivially_move_assignable<std::pair<CountAssign, int>>::value, "");
141+
static_assert(!std::is_trivially_move_assignable<std::pair<int, CountAssign>>::value, "");
142+
static_assert(!std::is_trivially_move_assignable<std::pair<CountAssign, CountAssign>>::value, "");
143+
133144
return true;
134145
}
135146

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++03, c++11, c++14, c++17
10+
11+
// <utility>
12+
13+
// template <class T1, class T2> struct pair
14+
15+
// Checks that `std::pair` is actually trivially copyable.
16+
17+
#include <type_traits>
18+
#include <utility>
19+
20+
#if defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
21+
static_assert(std::is_trivially_copyable_v<std::pair<int, int>>);
22+
static_assert(std::is_trivially_copyable_v<std::pair<int, int const>>);
23+
static_assert(std::is_trivially_copyable_v<std::pair<int const, int>>);
24+
static_assert(std::is_trivially_copyable_v<std::pair<int const, int const>>);
25+
26+
static_assert(!std::is_trivially_copyable_v<std::pair<int, int&>>);
27+
static_assert(!std::is_trivially_copyable_v<std::pair<int&, int>>);
28+
static_assert(!std::is_trivially_copyable_v<std::pair<int&, int&>>);
29+
30+
enum E {};
31+
static_assert(std::is_trivially_copyable_v<std::pair<E, E>>);
32+
static_assert(std::is_trivially_copyable_v<std::pair<E, E const>>);
33+
static_assert(std::is_trivially_copyable_v<std::pair<E const, E>>);
34+
static_assert(std::is_trivially_copyable_v<std::pair<E const, E const>>);
35+
36+
static_assert(std::is_trivially_copyable_v<std::pair<E, int>>);
37+
static_assert(std::is_trivially_copyable_v<std::pair<E, int const>>);
38+
static_assert(std::is_trivially_copyable_v<std::pair<E const, int>>);
39+
static_assert(std::is_trivially_copyable_v<std::pair<E const, int const>>);
40+
41+
struct S {};
42+
static_assert(std::is_trivially_copyable_v<std::pair<S, S>>);
43+
static_assert(std::is_trivially_copyable_v<std::pair<S, int>>);
44+
static_assert(std::is_trivially_copyable_v<std::pair<S, E>>);
45+
#endif

0 commit comments

Comments
 (0)