Fix non-conformance it `std::tuple`.
Previously we implemented all one trillion tuple-like constructors using
a single generic overload. This worked fairly well, except that it
differed in behavior from the standard version because it didn't
consider both T&& and T const&. This was observable for certain
types.
This patch addresses that issue by splitting the generic constructor
in two. We now provide both T&& and T const& versions of the
tuple-like constructors (sort of).
git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@365973 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/tuple b/include/tuple
index de30e86..031d25a 100644
--- a/include/tuple
+++ b/include/tuple
@@ -601,6 +601,25 @@
}
};
+ template <class _Tuple, bool _DisableIfLValue>
+ using _EnableImplicitTupleLikeConstructor = _EnableIf<
+ _CheckTupleLikeConstructor<
+ __tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value
+ && !_PackExpandsToThisTuple<_Tuple>::value
+ && (!is_lvalue_reference<_Tuple>::value || !_DisableIfLValue)
+ >::template __enable_implicit<_Tuple>(),
+ bool
+ >;
+
+ template <class _Tuple, bool _DisableIfLValue>
+ using _EnableExplicitTupleLikeConstructor = _EnableIf<
+ _CheckTupleLikeConstructor<
+ __tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value
+ && !_PackExpandsToThisTuple<_Tuple>::value
+ && (!is_lvalue_reference<_Tuple>::value || !_DisableIfLValue)
+ >::template __enable_explicit<_Tuple>(),
+ bool
+ >;
template <size_t _Jp, class ..._Up> friend _LIBCPP_CONSTEXPR_AFTER_CXX11
typename tuple_element<_Jp, tuple<_Up...> >::type& get(tuple<_Up...>&) _NOEXCEPT;
template <size_t _Jp, class ..._Up> friend _LIBCPP_CONSTEXPR_AFTER_CXX11
@@ -815,35 +834,27 @@
typename __make_tuple_types<tuple, sizeof...(_Tp), sizeof...(_Up)>::type(),
_VSTD::forward<_Up>(__u)...) {}
- template <class _Tuple,
- typename enable_if
- <
- _CheckTupleLikeConstructor<
- __tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value
- && !_PackExpandsToThisTuple<_Tuple>::value
- >::template __enable_implicit<_Tuple>(),
- bool
- >::type = false
- >
+ template <class _Tuple, _EnableImplicitTupleLikeConstructor<_Tuple, true> = false>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
tuple(_Tuple&& __t) _NOEXCEPT_((is_nothrow_constructible<_BaseT, _Tuple>::value))
: __base_(_VSTD::forward<_Tuple>(__t)) {}
- template <class _Tuple,
- typename enable_if
- <
- _CheckTupleLikeConstructor<
- __tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value
- && !_PackExpandsToThisTuple<_Tuple>::value
- >::template __enable_explicit<_Tuple>(),
- bool
- >::type = false
- >
+ template <class _Tuple, _EnableImplicitTupleLikeConstructor<const _Tuple&, false> = false>
+ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+ tuple(const _Tuple& __t) _NOEXCEPT_((is_nothrow_constructible<_BaseT, const _Tuple&>::value))
+ : __base_(__t) {}
+ template <class _Tuple, _EnableExplicitTupleLikeConstructor<_Tuple, true> = false>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
explicit
tuple(_Tuple&& __t) _NOEXCEPT_((is_nothrow_constructible<_BaseT, _Tuple>::value))
: __base_(_VSTD::forward<_Tuple>(__t)) {}
+ template <class _Tuple, _EnableExplicitTupleLikeConstructor<const _Tuple&, false> = false>
+ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+ explicit
+ tuple(const _Tuple& __t) _NOEXCEPT_((is_nothrow_constructible<_BaseT, const _Tuple&>::value))
+ : __base_(__t) {}
+
template <class _Alloc, class _Tuple,
typename enable_if
<
diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_copy.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_copy.pass.cpp
index 41f7332..89f67a2 100644
--- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_copy.pass.cpp
+++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_copy.pass.cpp
@@ -31,6 +31,15 @@
Implicit(int x) : value(x) {}
};
+struct ExplicitTwo {
+ ExplicitTwo() {}
+ ExplicitTwo(ExplicitTwo const&) {}
+ ExplicitTwo(ExplicitTwo &&) {}
+
+ template <class T, class = typename std::enable_if<!std::is_same<T, ExplicitTwo>::value>::type>
+ explicit ExplicitTwo(T) {}
+};
+
struct B
{
int id_;
@@ -136,6 +145,13 @@
std::tuple<Implicit> t2 = t1;
assert(std::get<0>(t2).value == 42);
}
+ {
+ static_assert(std::is_convertible<ExplicitTwo&&, ExplicitTwo>::value, "");
+ static_assert(std::is_convertible<std::tuple<ExplicitTwo&&>&&, const std::tuple<ExplicitTwo>&>::value, "");
+ ExplicitTwo e;
+ std::tuple<ExplicitTwo> t = std::tuple<ExplicitTwo&&>(std::move(e));
+ ((void)t);
+ }
return 0;
}