Index: libcxx/trunk/include/tuple =================================================================== --- libcxx/trunk/include/tuple +++ libcxx/trunk/include/tuple @@ -384,6 +384,9 @@ : is_same<__all<_Pred...>, __all<(_Pred, true)...>> { }; +template +struct __lazy_all : __all<_Tp::value...> {}; + template struct __all_default_constructible; @@ -523,6 +526,19 @@ _LIBCPP_CONSTEXPR tuple() _NOEXCEPT_(__all::value...>::value) {} + template , + __lazy_all<__dependent_type, _Dummy>...> + >::value + >::type> + _LIBCPP_INLINE_VISIBILITY + tuple(_AllocArgT, _Alloc const& __a) + : base_(allocator_arg_t(), __a, + __tuple_indices<>(), __tuple_types<>(), + typename __make_tuple_indices::type(), + __tuple_types<_Tp...>()) {} + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 explicit tuple(const _Tp& ... __t) _NOEXCEPT_((__all::value...>::value)) : base_(typename __make_tuple_indices::type(), @@ -631,21 +647,8 @@ template , - typename __make_tuple_types::type - >::value && - __all_default_constructible< - typename __make_tuple_types::type - >::value + sizeof...(_Up) == sizeof...(_Tp) && + __tuple_convertible, tuple>::value >::type > _LIBCPP_INLINE_VISIBILITY Index: libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp =================================================================== --- libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp +++ libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp @@ -24,12 +24,28 @@ #include "../alloc_first.h" #include "../alloc_last.h" +template +struct NonDefaultConstructible { + constexpr NonDefaultConstructible() { + static_assert(!std::is_same::value, "Default Ctor instantiated"); + } + + explicit constexpr NonDefaultConstructible(int) {} +}; + + +struct DerivedFromAllocArgT : std::allocator_arg_t {}; + int main() { { std::tuple<> t(std::allocator_arg, A1()); } { + DerivedFromAllocArgT tag; + std::tuple<> t(tag, A1()); + } + { std::tuple t(std::allocator_arg, A1()); assert(std::get<0>(t) == 0); } @@ -78,4 +94,29 @@ assert(!alloc_last::allocator_constructed); assert(std::get<2>(t) == alloc_last()); } + { + // Test that allocator construction is selected when the user provides + // a custom tag type which derives from allocator_arg_t. + DerivedFromAllocArgT tag; + alloc_first::allocator_constructed = false; + alloc_last::allocator_constructed = false; + + std::tuple t(tag, A1(5)); + + assert(std::get<0>(t) == DefaultOnly()); + assert(alloc_first::allocator_constructed); + assert(std::get<1>(t) == alloc_first()); + assert(alloc_last::allocator_constructed); + assert(std::get<2>(t) == alloc_last()); + } + { + // Test that the uses-allocator default constructor does not evaluate + // it's SFINAE when it otherwise shouldn't be selected. Do this by + // using 'NonDefaultConstructible' which will cause a compile error + // if std::is_default_constructible is evaluated on it. + using T = NonDefaultConstructible<>; + T v(42); + std::tuple t(v, v); + std::tuple t2(42, 42); + } } Index: libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_UTypes.pass.cpp =================================================================== --- libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_UTypes.pass.cpp +++ libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_UTypes.pass.cpp @@ -24,16 +24,29 @@ #include "../alloc_first.h" #include "../alloc_last.h" -struct NoDefault { NoDefault() = delete; }; +template +struct DefaultCtorBlowsUp { + constexpr DefaultCtorBlowsUp() { + static_assert(!std::is_same::value, "Default Ctor instantiated"); + } -// Make sure the _Up... constructor SFINAEs out when the types that -// are not explicitly initialized are not all default constructible. -// Otherwise, std::is_constructible would return true but instantiating -// the constructor would fail. -void test_default_constructible_extension_sfinae() + explicit constexpr DefaultCtorBlowsUp(int x) : value(x) {} + + int value; +}; + + +struct DerivedFromAllocArgT : std::allocator_arg_t {}; + + +// Make sure the _Up... constructor SFINAEs out when the number of initializers +// is less that the number of elements in the tuple. Previously libc++ would +// offer these constructers as an extension but they broke conforming code. +void test_uses_allocator_sfinae_evaluation() { + using BadDefault = DefaultCtorBlowsUp<>; { - typedef std::tuple Tuple; + typedef std::tuple Tuple; static_assert(!std::is_constructible< Tuple, @@ -42,11 +55,11 @@ static_assert(std::is_constructible< Tuple, - std::allocator_arg_t, A1, MoveOnly, NoDefault + std::allocator_arg_t, A1, MoveOnly, MoveOnly, BadDefault >::value, ""); } { - typedef std::tuple Tuple; + typedef std::tuple Tuple; static_assert(!std::is_constructible< Tuple, @@ -55,36 +68,7 @@ static_assert(std::is_constructible< Tuple, - std::allocator_arg_t, A1, MoveOnly, MoveOnly, NoDefault - >::value, ""); - } - { - // Same idea as above but with a nested tuple - typedef std::tuple Tuple; - typedef std::tuple NestedTuple; - - static_assert(!std::is_constructible< - NestedTuple, - std::allocator_arg_t, A1, MoveOnly, MoveOnly, MoveOnly, MoveOnly - >::value, ""); - - static_assert(std::is_constructible< - NestedTuple, - std::allocator_arg_t, A1, MoveOnly, Tuple, MoveOnly, MoveOnly - >::value, ""); - } - { - typedef std::tuple Tuple; - typedef std::tuple NestedTuple; - - static_assert(std::is_constructible< - NestedTuple, - std::allocator_arg_t, A1, MoveOnly, MoveOnly, MoveOnly, MoveOnly - >::value, ""); - - static_assert(std::is_constructible< - NestedTuple, - std::allocator_arg_t, A1, MoveOnly, Tuple, MoveOnly, MoveOnly + std::allocator_arg_t, A1, MoveOnly, MoveOnly, BadDefault, BadDefault >::value, ""); } } @@ -96,12 +80,23 @@ assert(std::get<0>(t) == 0); } { + using T = DefaultCtorBlowsUp<>; + std::tuple t(std::allocator_arg, A1(), T(42)); + assert(std::get<0>(t).value == 42); + } + { std::tuple t(std::allocator_arg, A1(), MoveOnly(0), MoveOnly(1)); assert(std::get<0>(t) == 0); assert(std::get<1>(t) == 1); } { + using T = DefaultCtorBlowsUp<>; + std::tuple t(std::allocator_arg, A1(), T(42), T(43)); + assert(std::get<0>(t).value == 42); + assert(std::get<1>(t).value == 43); + } + { std::tuple t(std::allocator_arg, A1(), MoveOnly(0), 1, 2); @@ -110,6 +105,13 @@ assert(std::get<2>(t) == 2); } { + using T = DefaultCtorBlowsUp<>; + std::tuple t(std::allocator_arg, A1(), T(1), T(2), T(3)); + assert(std::get<0>(t).value == 1); + assert(std::get<1>(t).value == 2); + assert(std::get<2>(t).value == 3); + } + { alloc_first::allocator_constructed = false; alloc_last::allocator_constructed = false; std::tuple t(std::allocator_arg, @@ -120,22 +122,22 @@ assert(alloc_last::allocator_constructed); assert(std::get<2>(t) == alloc_last(3)); } - // extensions - { - std::tuple t(std::allocator_arg, A1(), - 0, 1); - assert(std::get<0>(t) == 0); - assert(std::get<1>(t) == 1); - assert(std::get<2>(t) == MoveOnly()); - } { - std::tuple t(std::allocator_arg, A1(), - 0); - assert(std::get<0>(t) == 0); - assert(std::get<1>(t) == MoveOnly()); - assert(std::get<2>(t) == MoveOnly()); + // Check that uses-allocator construction is still selected when + // given a tag type that derives from allocator_arg_t. + DerivedFromAllocArgT tag; + alloc_first::allocator_constructed = false; + alloc_last::allocator_constructed = false; + std::tuple t(tag, + A1(5), 1, 2, 3); + assert(std::get<0>(t) == 1); + assert(alloc_first::allocator_constructed); + assert(std::get<1>(t) == alloc_first(2)); + assert(alloc_last::allocator_constructed); + assert(std::get<2>(t) == alloc_last(3)); } - // Check that SFINAE is properly applied with the default reduced arity - // constructor extensions. - test_default_constructible_extension_sfinae(); + // Stress test the SFINAE on the uses-allocator constructors and + // ensure that the "reduced-arity-initialization" extension is not offered + // for these constructors. + test_uses_allocator_sfinae_evaluation(); }