Revert r276548 - Make pair/tuples assignment operators SFINAE properly.
This is a breaking change. The SFINAE required is instantiated the second the class is instantiated, and this can cause hard SFINAE errors when applied to references to incomplete types. Ex. struct IncompleteType; extern IncompleteType it; std::tuple<IncompleteType&> t(it); // SFINAE will blow up. git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@276598 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -647,9 +647,6 @@ 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>,
|
||||
@@ -888,25 +885,6 @@ 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,19 +348,8 @@ struct _LIBCPP_TYPE_VIS_ONLY pair
|
||||
// Use the implicitly declared copy constructor in C++03
|
||||
#endif
|
||||
|
||||
typedef typename remove_reference<_T1>::type _T1Unref;
|
||||
typedef typename remove_reference<_T2>::type _T2Unref;
|
||||
|
||||
#if !defined(_LIBCPP_CXX03_LANG)
|
||||
typedef integral_constant<bool,
|
||||
is_copy_assignable<_T1>::value
|
||||
&& is_copy_assignable<_T2>::value> _CanCopyAssign;
|
||||
#else
|
||||
typedef true_type _CanCopyAssign;
|
||||
#endif
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
pair& operator=(typename conditional<_CanCopyAssign::value, pair, __nat>::type const& __p)
|
||||
pair& operator=(const pair& __p)
|
||||
_NOEXCEPT_(is_nothrow_copy_assignable<first_type>::value &&
|
||||
is_nothrow_copy_assignable<second_type>::value)
|
||||
{
|
||||
@@ -388,15 +377,10 @@ struct _LIBCPP_TYPE_VIS_ONLY pair
|
||||
: first(_VSTD::forward<_U1>(__p.first)),
|
||||
second(_VSTD::forward<_U2>(__p.second)) {}
|
||||
|
||||
typedef integral_constant<bool,
|
||||
is_move_assignable<_T1>::value
|
||||
&& is_move_assignable<_T2>::value> _CanMoveAssign;
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
pair&
|
||||
operator=(typename conditional<_CanMoveAssign::value, pair, __nat>::type&& __p)
|
||||
_NOEXCEPT_(is_nothrow_move_assignable<first_type>::value &&
|
||||
is_nothrow_move_assignable<second_type>::value)
|
||||
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);
|
||||
@@ -404,6 +388,7 @@ struct _LIBCPP_TYPE_VIS_ONLY pair
|
||||
}
|
||||
|
||||
#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
|
||||
@@ -426,7 +411,7 @@ struct _LIBCPP_TYPE_VIS_ONLY pair
|
||||
{}
|
||||
|
||||
template <class _Tuple,
|
||||
class = typename enable_if<!is_same<typename decay<_Tuple>::type, pair>::value && __tuple_assignable<_Tuple, pair>::value>::type>
|
||||
class = typename enable_if<__tuple_assignable<_Tuple, pair>::value>::type>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
pair&
|
||||
operator=(_Tuple&& __p)
|
||||
|
||||
@@ -19,22 +19,6 @@
|
||||
#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()
|
||||
{
|
||||
{
|
||||
@@ -67,37 +51,4 @@ 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,33 +21,6 @@
|
||||
|
||||
#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()
|
||||
{
|
||||
{
|
||||
@@ -80,46 +53,4 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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;
|
||||
|
||||
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, "");
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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&);
|
||||
};
|
||||
|
||||
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, "");
|
||||
}
|
||||
@@ -7,8 +7,6 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// <utility>
|
||||
|
||||
// template <class T1, class T2> struct pair
|
||||
@@ -19,35 +17,9 @@
|
||||
#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);
|
||||
@@ -56,41 +28,5 @@ int main()
|
||||
assert(*p2.first == 3);
|
||||
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 = 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);
|
||||
}
|
||||
#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user