forked from OSchip/llvm-project
Recommit r276548 - Make pair/tuples assignment operators SFINAE properly.
I think I've solved issues with is_assignable and references to incomplete types. The updated patch adds tests for this case. llvm-svn: 276603
This commit is contained in:
parent
68623a0e9f
commit
aedcbf898b
|
@ -647,6 +647,9 @@ public:
|
|||
_LIBCPP_CONSTEXPR tuple()
|
||||
_NOEXCEPT_(__all<is_nothrow_default_constructible<_Tp>::value...>::value) {}
|
||||
|
||||
tuple(tuple const&) = default;
|
||||
tuple(tuple&&) = default;
|
||||
|
||||
template <class _AllocArgT, class _Alloc, bool _Dummy = true, class = typename enable_if<
|
||||
__lazy_and<
|
||||
is_same<allocator_arg_t, _AllocArgT>,
|
||||
|
@ -885,6 +888,25 @@ public:
|
|||
tuple(allocator_arg_t, const _Alloc& __a, _Tuple&& __t)
|
||||
: base_(allocator_arg_t(), __a, _VSTD::forward<_Tuple>(__t)) {}
|
||||
|
||||
using _CanCopyAssign = __all<is_copy_assignable<_Tp>::value...>;
|
||||
using _CanMoveAssign = __all<is_move_assignable<_Tp>::value...>;
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
tuple& operator=(typename conditional<_CanCopyAssign::value, tuple, __nat>::type const& __t)
|
||||
_NOEXCEPT_((__all<is_nothrow_copy_assignable<_Tp>::value...>::value))
|
||||
{
|
||||
base_.operator=(__t.base_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
tuple& operator=(typename conditional<_CanMoveAssign::value, tuple, __nat>::type&& __t)
|
||||
_NOEXCEPT_((__all<is_nothrow_move_assignable<_Tp>::value...>::value))
|
||||
{
|
||||
base_.operator=(static_cast<base&&>(__t.base_));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class _Tuple,
|
||||
class = typename enable_if
|
||||
<
|
||||
|
|
|
@ -348,8 +348,23 @@ struct _LIBCPP_TYPE_VIS_ONLY pair
|
|||
// Use the implicitly declared copy constructor in C++03
|
||||
#endif
|
||||
|
||||
#if !defined(_LIBCPP_CXX03_LANG)
|
||||
typedef typename conditional<
|
||||
is_copy_assignable<_T1>::value
|
||||
&& is_copy_assignable<_T2>::value,
|
||||
pair, __nat
|
||||
>::type _CopyAssignT;
|
||||
typedef typename conditional<
|
||||
is_move_assignable<_T1>::value
|
||||
&& is_move_assignable<_T2>::value,
|
||||
pair, __nat
|
||||
>::type _MoveAssignT;
|
||||
#else
|
||||
typedef pair _CopyAssignT;
|
||||
#endif
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
pair& operator=(const pair& __p)
|
||||
pair& operator=(_CopyAssignT const& __p)
|
||||
_NOEXCEPT_(is_nothrow_copy_assignable<first_type>::value &&
|
||||
is_nothrow_copy_assignable<second_type>::value)
|
||||
{
|
||||
|
@ -358,6 +373,18 @@ struct _LIBCPP_TYPE_VIS_ONLY pair
|
|||
return *this;
|
||||
}
|
||||
|
||||
#ifndef _LIBCPP_CXX03_LANG
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
pair& operator=(_MoveAssignT&& __p)
|
||||
_NOEXCEPT_(is_nothrow_move_assignable<first_type>::value &&
|
||||
is_nothrow_move_assignable<second_type>::value)
|
||||
{
|
||||
first = _VSTD::forward<first_type>(__p.first);
|
||||
second = _VSTD::forward<second_type>(__p.second);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
|
||||
template <class _U1, class _U2,
|
||||
|
@ -377,18 +404,9 @@ struct _LIBCPP_TYPE_VIS_ONLY pair
|
|||
: first(_VSTD::forward<_U1>(__p.first)),
|
||||
second(_VSTD::forward<_U2>(__p.second)) {}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
pair&
|
||||
operator=(pair&& __p) _NOEXCEPT_(is_nothrow_move_assignable<first_type>::value &&
|
||||
is_nothrow_move_assignable<second_type>::value)
|
||||
{
|
||||
first = _VSTD::forward<first_type>(__p.first);
|
||||
second = _VSTD::forward<second_type>(__p.second);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
#ifndef _LIBCPP_HAS_NO_VARIADICS
|
||||
|
||||
template<class _Tuple,
|
||||
class = typename enable_if<__tuple_convertible<_Tuple, pair>::value>::type>
|
||||
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
|
@ -411,7 +429,7 @@ struct _LIBCPP_TYPE_VIS_ONLY pair
|
|||
{}
|
||||
|
||||
template <class _Tuple,
|
||||
class = typename enable_if<__tuple_assignable<_Tuple, pair>::value>::type>
|
||||
class = typename enable_if<!is_same<typename decay<_Tuple>::type, pair>::value && __tuple_assignable<_Tuple, pair>::value>::type>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
pair&
|
||||
operator=(_Tuple&& __p)
|
||||
|
|
|
@ -19,6 +19,22 @@
|
|||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
struct NonAssignable {
|
||||
NonAssignable& operator=(NonAssignable const&) = delete;
|
||||
NonAssignable& operator=(NonAssignable&&) = delete;
|
||||
};
|
||||
struct CopyAssignable {
|
||||
CopyAssignable& operator=(CopyAssignable const&) = default;
|
||||
CopyAssignable& operator=(CopyAssignable &&) = delete;
|
||||
};
|
||||
static_assert(std::is_copy_assignable<CopyAssignable>::value, "");
|
||||
struct MoveAssignable {
|
||||
MoveAssignable& operator=(MoveAssignable const&) = delete;
|
||||
MoveAssignable& operator=(MoveAssignable&&) = default;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
|
@ -51,4 +67,37 @@ int main()
|
|||
assert(std::get<1>(t) == 'a');
|
||||
assert(std::get<2>(t) == "some text");
|
||||
}
|
||||
{
|
||||
// test reference assignment.
|
||||
using T = std::tuple<int&, int&&>;
|
||||
int x = 42;
|
||||
int y = 100;
|
||||
int x2 = -1;
|
||||
int y2 = 500;
|
||||
T t(x, std::move(y));
|
||||
T t2(x2, std::move(y2));
|
||||
t = t2;
|
||||
assert(std::get<0>(t) == x2);
|
||||
assert(&std::get<0>(t) == &x);
|
||||
assert(std::get<1>(t) == y2);
|
||||
assert(&std::get<1>(t) == &y);
|
||||
}
|
||||
{
|
||||
// test that the implicitly generated copy assignment operator
|
||||
// is properly deleted
|
||||
using T = std::tuple<std::unique_ptr<int>>;
|
||||
static_assert(!std::is_copy_assignable<T>::value, "");
|
||||
}
|
||||
{
|
||||
using T = std::tuple<int, NonAssignable>;
|
||||
static_assert(!std::is_copy_assignable<T>::value, "");
|
||||
}
|
||||
{
|
||||
using T = std::tuple<int, CopyAssignable>;
|
||||
static_assert(std::is_copy_assignable<T>::value, "");
|
||||
}
|
||||
{
|
||||
using T = std::tuple<int, MoveAssignable>;
|
||||
static_assert(!std::is_copy_assignable<T>::value, "");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,33 @@
|
|||
|
||||
#include "MoveOnly.h"
|
||||
|
||||
struct NonAssignable {
|
||||
NonAssignable& operator=(NonAssignable const&) = delete;
|
||||
NonAssignable& operator=(NonAssignable&&) = delete;
|
||||
};
|
||||
struct CopyAssignable {
|
||||
CopyAssignable& operator=(CopyAssignable const&) = default;
|
||||
CopyAssignable& operator=(CopyAssignable&&) = delete;
|
||||
};
|
||||
static_assert(std::is_copy_assignable<CopyAssignable>::value, "");
|
||||
struct MoveAssignable {
|
||||
MoveAssignable& operator=(MoveAssignable const&) = delete;
|
||||
MoveAssignable& operator=(MoveAssignable&&) = default;
|
||||
};
|
||||
|
||||
|
||||
struct CountAssign {
|
||||
static int copied;
|
||||
static int moved;
|
||||
static void reset() { copied = moved = 0; }
|
||||
CountAssign() = default;
|
||||
CountAssign& operator=(CountAssign const&) { ++copied; return *this; }
|
||||
CountAssign& operator=(CountAssign&&) { ++moved; return *this; }
|
||||
};
|
||||
int CountAssign::copied = 0;
|
||||
int CountAssign::moved = 0;
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
|
@ -53,4 +80,46 @@ int main()
|
|||
assert(std::get<1>(t) == 1);
|
||||
assert(std::get<2>(t) == 2);
|
||||
}
|
||||
{
|
||||
// test reference assignment.
|
||||
using T = std::tuple<int&, int&&>;
|
||||
int x = 42;
|
||||
int y = 100;
|
||||
int x2 = -1;
|
||||
int y2 = 500;
|
||||
T t(x, std::move(y));
|
||||
T t2(x2, std::move(y2));
|
||||
t = std::move(t2);
|
||||
assert(std::get<0>(t) == x2);
|
||||
assert(&std::get<0>(t) == &x);
|
||||
assert(std::get<1>(t) == y2);
|
||||
assert(&std::get<1>(t) == &y);
|
||||
}
|
||||
{
|
||||
// test that the implicitly generated move assignment operator
|
||||
// is properly deleted
|
||||
using T = std::tuple<std::unique_ptr<int>>;
|
||||
static_assert(std::is_move_assignable<T>::value, "");
|
||||
static_assert(!std::is_copy_assignable<T>::value, "");
|
||||
|
||||
}
|
||||
{
|
||||
using T = std::tuple<int, NonAssignable>;
|
||||
static_assert(!std::is_move_assignable<T>::value, "");
|
||||
}
|
||||
{
|
||||
using T = std::tuple<int, MoveAssignable>;
|
||||
static_assert(std::is_move_assignable<T>::value, "");
|
||||
}
|
||||
{
|
||||
// The move should decay to a copy.
|
||||
CountAssign::reset();
|
||||
using T = std::tuple<CountAssign, CopyAssignable>;
|
||||
static_assert(std::is_move_assignable<T>::value, "");
|
||||
T t1;
|
||||
T t2;
|
||||
t1 = std::move(t2);
|
||||
assert(CountAssign::copied == 1);
|
||||
assert(CountAssign::moved == 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// <utility>
|
||||
|
||||
// template <class T1, class T2> struct pair
|
||||
|
||||
// pair& operator=(pair const& p);
|
||||
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
struct NonAssignable {
|
||||
NonAssignable& operator=(NonAssignable const&) = delete;
|
||||
NonAssignable& operator=(NonAssignable&&) = delete;
|
||||
};
|
||||
struct CopyAssignable {
|
||||
CopyAssignable() = default;
|
||||
CopyAssignable(CopyAssignable const&) = default;
|
||||
CopyAssignable& operator=(CopyAssignable const&) = default;
|
||||
CopyAssignable& operator=(CopyAssignable&&) = delete;
|
||||
};
|
||||
struct MoveAssignable {
|
||||
MoveAssignable() = default;
|
||||
MoveAssignable& operator=(MoveAssignable const&) = delete;
|
||||
MoveAssignable& operator=(MoveAssignable&&) = default;
|
||||
};
|
||||
|
||||
struct CountAssign {
|
||||
static int copied;
|
||||
static int moved;
|
||||
static void reset() { copied = moved = 0; }
|
||||
CountAssign() = default;
|
||||
CountAssign& operator=(CountAssign const&) { ++copied; return *this; }
|
||||
CountAssign& operator=(CountAssign&&) { ++moved; return *this; }
|
||||
};
|
||||
int CountAssign::copied = 0;
|
||||
int CountAssign::moved = 0;
|
||||
|
||||
struct Incomplete;
|
||||
extern Incomplete inc_obj;
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
typedef std::pair<CopyAssignable, short> P;
|
||||
const P p1(CopyAssignable(), 4);
|
||||
P p2;
|
||||
p2 = p1;
|
||||
assert(p2.second == 4);
|
||||
}
|
||||
{
|
||||
using P = std::pair<int&, int&&>;
|
||||
int x = 42;
|
||||
int y = 101;
|
||||
int x2 = -1;
|
||||
int y2 = 300;
|
||||
P p1(x, std::move(y));
|
||||
P p2(x2, std::move(y2));
|
||||
p1 = p2;
|
||||
assert(p1.first == x2);
|
||||
assert(p1.second == y2);
|
||||
}
|
||||
{
|
||||
using P = std::pair<int, NonAssignable>;
|
||||
static_assert(!std::is_copy_assignable<P>::value, "");
|
||||
}
|
||||
{
|
||||
CountAssign::reset();
|
||||
using P = std::pair<CountAssign, CopyAssignable>;
|
||||
static_assert(std::is_copy_assignable<P>::value, "");
|
||||
P p;
|
||||
P p2;
|
||||
p = p2;
|
||||
assert(CountAssign::copied == 1);
|
||||
assert(CountAssign::moved == 0);
|
||||
}
|
||||
{
|
||||
using P = std::pair<int, MoveAssignable>;
|
||||
static_assert(!std::is_copy_assignable<P>::value, "");
|
||||
}
|
||||
{
|
||||
using P = std::pair<int, Incomplete&>;
|
||||
static_assert(!std::is_copy_assignable<P>::value, "");
|
||||
P p(42, inc_obj);
|
||||
assert(&p.second == &inc_obj);
|
||||
}
|
||||
}
|
||||
|
||||
struct Incomplete {};
|
||||
Incomplete inc_obj;
|
|
@ -0,0 +1,49 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES-ANY: c++98, c++03
|
||||
|
||||
// <utility>
|
||||
|
||||
// template <class T1, class T2> struct pair
|
||||
|
||||
// pair& operator=(pair const& p);
|
||||
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
|
||||
struct NonAssignable {
|
||||
NonAssignable() {}
|
||||
private:
|
||||
NonAssignable& operator=(NonAssignable const&);
|
||||
};
|
||||
|
||||
struct Incomplete;
|
||||
extern Incomplete inc_obj;
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
struct Incomplete {};
|
||||
Incomplete inc_obj;
|
|
@ -7,6 +7,8 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// <utility>
|
||||
|
||||
// template <class T1, class T2> struct pair
|
||||
|
@ -17,9 +19,35 @@
|
|||
#include <memory>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
struct NonAssignable {
|
||||
NonAssignable& operator=(NonAssignable const&) = delete;
|
||||
NonAssignable& operator=(NonAssignable&&) = delete;
|
||||
};
|
||||
struct CopyAssignable {
|
||||
CopyAssignable() = default;
|
||||
CopyAssignable& operator=(CopyAssignable const&) = default;
|
||||
CopyAssignable& operator=(CopyAssignable&&) = delete;
|
||||
};
|
||||
struct MoveAssignable {
|
||||
MoveAssignable() = default;
|
||||
MoveAssignable& operator=(MoveAssignable const&) = delete;
|
||||
MoveAssignable& operator=(MoveAssignable&&) = default;
|
||||
};
|
||||
|
||||
struct CountAssign {
|
||||
static int copied;
|
||||
static int moved;
|
||||
static void reset() { copied = moved = 0; }
|
||||
CountAssign() = default;
|
||||
CountAssign& operator=(CountAssign const&) { ++copied; return *this; }
|
||||
CountAssign& operator=(CountAssign&&) { ++moved; return *this; }
|
||||
};
|
||||
int CountAssign::copied = 0;
|
||||
int CountAssign::moved = 0;
|
||||
|
||||
int main()
|
||||
{
|
||||
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
{
|
||||
typedef std::pair<std::unique_ptr<int>, short> P;
|
||||
P p1(std::unique_ptr<int>(new int(3)), 4);
|
||||
|
@ -28,5 +56,41 @@ int main()
|
|||
assert(*p2.first == 3);
|
||||
assert(p2.second == 4);
|
||||
}
|
||||
#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
{
|
||||
using P = std::pair<int&, int&&>;
|
||||
int x = 42;
|
||||
int y = 101;
|
||||
int x2 = -1;
|
||||
int y2 = 300;
|
||||
P p1(x, std::move(y));
|
||||
P p2(x2, std::move(y2));
|
||||
p1 = std::move(p2);
|
||||
assert(p1.first == x2);
|
||||
assert(p1.second == y2);
|
||||
}
|
||||
{
|
||||
using P = std::pair<int, NonAssignable>;
|
||||
static_assert(!std::is_move_assignable<P>::value, "");
|
||||
}
|
||||
{
|
||||
// The move decays to the copy constructor
|
||||
CountAssign::reset();
|
||||
using P = std::pair<CountAssign, CopyAssignable>;
|
||||
static_assert(std::is_move_assignable<P>::value, "");
|
||||
P p;
|
||||
P p2;
|
||||
p = std::move(p2);
|
||||
assert(CountAssign::moved == 0);
|
||||
assert(CountAssign::copied == 1);
|
||||
}
|
||||
{
|
||||
CountAssign::reset();
|
||||
using P = std::pair<CountAssign, MoveAssignable>;
|
||||
static_assert(std::is_move_assignable<P>::value, "");
|
||||
P p;
|
||||
P p2;
|
||||
p = std::move(p2);
|
||||
assert(CountAssign::moved == 1);
|
||||
assert(CountAssign::copied == 0);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue