Index: include/__tuple =================================================================== --- include/__tuple +++ include/__tuple @@ -27,6 +27,32 @@ _LIBCPP_BEGIN_NAMESPACE_STD +// __lazy_and + +template +struct __lazy_and_impl; + +template +struct __lazy_and_impl : false_type {}; + +template <> +struct __lazy_and_impl : true_type {}; + +template +struct __lazy_and_impl : integral_constant {}; + +template +struct __lazy_and_impl : __lazy_and_impl<_Hp::type::value, _Tp...> {}; + +template +struct __lazy_and : __lazy_and_impl<_P1::type::value, _Pr...> {}; + +// __lazy_not + +template +struct __lazy_not : integral_constant {}; + + template class _LIBCPP_TYPE_VIS_ONLY tuple_size; template Index: include/tuple =================================================================== --- include/tuple +++ include/tuple @@ -210,7 +210,13 @@ "Attempted to default construct a reference element in a tuple");} template ::value>::type> + class = typename enable_if< + __lazy_and< + __lazy_not::type, __tuple_leaf>> + , is_constructible<_Hp, _Tp> + >::value + >::type + > _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 explicit __tuple_leaf(_Tp&& __t) _NOEXCEPT_((is_nothrow_constructible<_Hp, _Tp>::value)) : value(_VSTD::forward<_Tp>(__t)) @@ -316,7 +322,13 @@ : _Hp(__a) {} template ::value>::type> + class = typename enable_if< + __lazy_and< + __lazy_not::type, __tuple_leaf>> + , is_constructible<_Hp, _Tp> + >::value + >::type + > _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 explicit __tuple_leaf(_Tp&& __t) _NOEXCEPT_((is_nothrow_constructible<_Hp, _Tp>::value)) : _Hp(_VSTD::forward<_Tp>(__t)) {} @@ -336,6 +348,9 @@ explicit __tuple_leaf(integral_constant, const _Alloc& __a, _Tp&& __t) : _Hp(_VSTD::forward<_Tp>(__t), __a) {} + __tuple_leaf(__tuple_leaf const &) = default; + __tuple_leaf(__tuple_leaf &&) = default; + template _LIBCPP_INLINE_VISIBILITY __tuple_leaf& Index: test/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp =================================================================== --- test/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp +++ test/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp @@ -17,6 +17,40 @@ #include #include + +template +struct never { + enum { value = 0 }; +}; + +struct NoValueCtor +{ + NoValueCtor() : id(++count) {} + NoValueCtor(NoValueCtor const & other) : id(other.id) { ++count; } + + // The constexpr is required to make is_constructible instantiate this template. + // The explicit is needed to test-around a similar bug with is_convertible. + template + constexpr explicit NoValueCtor(T) + { static_assert(never::value, "This should not be instantiated"); } + + static int count; + int id; +}; + +int NoValueCtor::count = 0; + + +struct NoValueCtorEmpty +{ + NoValueCtorEmpty() {} + NoValueCtorEmpty(NoValueCtorEmpty const &) {} + + template + constexpr explicit NoValueCtorEmpty(T) + { static_assert(never::value, "This should not be instantiated"); } +}; + int main() { { @@ -56,6 +90,23 @@ assert(std::get<1>(t) == nullptr); assert(std::get<2>(t) == "text"); } + // __tuple_leaf uses is_constructible to disable its explicit converting + // constructor overload __tuple_leaf(U &&). Evaluating is_constructible can cause a compile error. + // This overload is evaluated when __tuple_leafs copy or move ctor is called. + // This checks that is_constructible is not evaluated when U == __tuple_leaf. + { + std::tuple t(1, NoValueCtor(), 2, 3); + assert(std::get<0>(t) == 1); + assert(std::get<1>(t).id == 1); + assert(std::get<2>(t) == 2); + assert(std::get<3>(t) == 3); + } + { + std::tuple t(1, NoValueCtorEmpty(), 2, 3); + assert(std::get<0>(t) == 1); + assert(std::get<2>(t) == 2); + assert(std::get<3>(t) == 3); + } // extensions { std::tuple t(2); Index: test/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp =================================================================== --- test/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp +++ test/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp @@ -18,6 +18,18 @@ #include "../MoveOnly.h" +struct ConstructsWithTupleLeaf +{ + ConstructsWithTupleLeaf() {} + + ConstructsWithTupleLeaf(ConstructsWithTupleLeaf const &) { assert(false); } + ConstructsWithTupleLeaf(ConstructsWithTupleLeaf &&) {} + + template + ConstructsWithTupleLeaf(T t) + { assert(false); } +}; + int main() { { @@ -46,4 +58,12 @@ assert(std::get<1>(t) == 1); assert(std::get<2>(t) == 2); } + // A bug in tuple caused __tuple_leaf to use its explicit converting constructor + // as its move constructor. This tests that ConstructsWithTupleLeaf is not called + // (w/ __tuple_leaf) + { + typedef std::tuple d_t; + d_t d((ConstructsWithTupleLeaf())); + d_t d2(static_cast(d)); + } } Index: test/utilities/tuple/tuple.tuple/tuple.elem/tuple.by.type1.fail.cpp =================================================================== --- test/utilities/tuple/tuple.tuple/tuple.elem/tuple.by.type1.fail.cpp +++ test/utilities/tuple/tuple.tuple/tuple.elem/tuple.by.type1.fail.cpp @@ -18,7 +18,7 @@ #if _LIBCPP_STD_VER > 11 typedef std::complex cf; auto t1 = std::make_tuple ( 42, "Hi" ); - assert ( std::get(t1) == cf {1,2} ); // no such type + assert (( std::get(t1) == cf {1,2} )); // no such type #else #error #endif Index: test/utilities/utility/pairs/pair.astuple/pairs.by.type1.fail.cpp =================================================================== --- test/utilities/utility/pairs/pair.astuple/pairs.by.type1.fail.cpp +++ test/utilities/utility/pairs/pair.astuple/pairs.by.type1.fail.cpp @@ -17,7 +17,7 @@ #if _LIBCPP_STD_VER > 11 typedef std::complex cf; auto t1 = std::make_pair ( 42, 3.4 ); - assert ( std::get(t1) == cf {1,2} ); // no such type + assert (( std::get(t1) == cf {1,2} )); // no such type #else #error #endif