diff options
author | Louis Dionne <ldionne.2@gmail.com> | 2023-05-08 09:50:57 -0400 |
---|---|---|
committer | Louis Dionne <ldionne.2@gmail.com> | 2023-05-09 08:35:16 -0400 |
commit | 0acafcd8043412b028be89ce68ce301ce329d3eb (patch) | |
tree | 68b424a4e8ea3219a566d61062899e627321f54e /libcxx | |
parent | 438e1cff7bfba1693db9fe66e848efe85232c992 (diff) | |
download | llvm-0acafcd8043412b028be89ce68ce301ce329d3eb.tar.gz |
[libc++] Provide an assignment operator from pair<U, V> in C++03
This adds an extension to std::pair in C++03 mode where we provide an
assignment operator from a pair<U, V>. Previously, any code trying to
trigger such an assignment operator would have tried using the
`operator=(pair const&)` copy assignment operator, which would then
have tried creating a `pair const&` by using the unconstrained
pair<U, V> constructor.
After this patch, pair will instead go through operator= directly if
its member types are assignable. If they are not assignable, the extension
operator= is disabled with SFINAE and the pair(pair<U, V>) constructor
will be used. Since that constructor is unconstrained, that will either
work or give a hard error.
This should be pretty transparent to users, but this is technically a
behavior change in C++03. Indeed, if a type has both a valid cross-type
assignment operator *and* a valid cross-type constructor, the library
will now prefer the cross-type assignment instead of going through the
cross-type constructor and then using the copy-constructor. Since this
is the mandated behavior in C++11, however, one could argue that any user
broken by that needs to change their code.
The motivation for this change is to allow simplifying the definition
of std::map's value_type, which requires handling assignment to a pair
of references properly. This patch will allow removing complexity from
https://llvm.org/D121485 instead of adding complexity in that patch.
Differential Revision: https://reviews.llvm.org/D150119
Diffstat (limited to 'libcxx')
3 files changed, 50 insertions, 9 deletions
diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h index 0d009061575b..65a4cc078023 100644 --- a/libcxx/include/__utility/pair.h +++ b/libcxx/include/__utility/pair.h @@ -105,6 +105,20 @@ struct _LIBCPP_TEMPLATE_VIS pair second = __p.second; return *this; } + + // Extension: This is provided in C++03 because it allows properly handling the + // assignment to a pair containing references, which would be a hard + // error otherwise. + template <class _U1, class _U2, class = __enable_if_t< + is_assignable<first_type&, _U1 const&>::value && + is_assignable<second_type&, _U2 const&>::value + > > + _LIBCPP_HIDE_FROM_ABI + pair& operator=(pair<_U1, _U2> const& __p) { + first = __p.first; + second = __p.second; + return *this; + } #else struct _CheckArgs { template <int&...> diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_pair_U_V.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_pair_U_V.pass.cpp index 2df3372493b3..d7303e556042 100644 --- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_pair_U_V.pass.cpp +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_pair_U_V.pass.cpp @@ -75,6 +75,16 @@ TEST_CONSTEXPR_CXX20 bool test() { static_assert(!std::is_assignable<P&, T&>::value, ""); } #endif +#if TEST_STD_VER >= 11 || defined(_LIBCPP_VERSION) // valid in C++11, provided in C++03 with libc++ as an extension + { + int i = 0, j = 0; + std::pair<int&, int&> p(i, j); + const std::pair<const int, const int> from(11, 12); + p = from; + assert(i == 11); + assert(j == 12); + } +#endif return true; } diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair_cxx03.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair_cxx03.pass.cpp index 4c076c869794..2444131b02a0 100644 --- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair_cxx03.pass.cpp +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair_cxx03.pass.cpp @@ -29,20 +29,37 @@ private: struct Incomplete; extern Incomplete inc_obj; +struct ConstructibleFromInt { + ConstructibleFromInt() : value(-1) { } + explicit ConstructibleFromInt(int v) : value(v) { } + int value; +}; + int main(int, char**) { { - // Test that we don't constrain the assignment operator in C++03 mode. - // Since we don't have access control SFINAE having pair evaluate SFINAE - // may cause a hard error. - typedef std::pair<int, NonAssignable> P; - static_assert(std::is_copy_assignable<P>::value, ""); + // Test that we don't constrain the assignment operator in C++03 mode. + // Since we don't have access control SFINAE having pair evaluate SFINAE + // may cause a hard error. + typedef std::pair<int, NonAssignable> P; + static_assert(std::is_copy_assignable<P>::value, ""); + } + { + typedef std::pair<int, Incomplete&> P; + static_assert(std::is_copy_assignable<P>::value, ""); + P p(42, inc_obj); + assert(&p.second == &inc_obj); } { - typedef std::pair<int, Incomplete&> P; - static_assert(std::is_copy_assignable<P>::value, ""); - P p(42, inc_obj); - assert(&p.second == &inc_obj); + // The type is constructible from int, but not assignable from int. + // This ensures that operator=(pair const&) can be used in conjunction with + // pair(pair<U, V> const&) to mimic operator=(pair<U, V> const&) in C++03. + // This is weird but valid in C++03. + std::pair<ConstructibleFromInt, char> p; + std::pair<int, char> from(11, 'x'); + p = from; + assert(p.first.value == 11); + assert(p.second == 'x'); } return 0; |