On 01/03/19 13:50 +0000, Jonathan Wakely wrote:
* include/std/memory (uses_allocator_construction_args): New set of overloaded functions. (make_obj_using_allocator, uninitialized_construct_using_allocator): New functions. * include/std/memory_resource (polymorphic_allocator::construct) [__cplusplus > 201703l]: Replace all overloads with a single function using uses_allocator_construction_args. * testsuite/20_util/polymorphic_allocator/construct_c++2a.cc: New test. * testsuite/20_util/uses_allocator/make_obj.cc: New test.
If we don't care about providing the exact signatures from the C++2a draft, we could do this and use it in C++17 as well ...
diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory index 00a85eef25e..045974a1b46 100644 --- a/libstdc++-v3/include/std/memory +++ b/libstdc++-v3/include/std/memory @@ -168,7 +168,7 @@ get_pointer_safety() noexcept { return pointer_safety::relaxed; } } #endif // C++2a -#if __cplusplus > 201703L +#if __cplusplus >= 201703L template<typename _Tp> struct __is_pair : false_type { }; template<typename _Tp, typename _Up> @@ -176,167 +176,94 @@ get_pointer_safety() noexcept { return pointer_safety::relaxed; } template<typename _Tp, typename _Up> struct __is_pair<const pair<_Tp, _Up>> : true_type { }; - template<typename _Tp, typename __ = _Require<__not_<__is_pair<_Tp>>>, - typename _Alloc, typename... _Args> + // Equivalent of uses_allocator_construction_args for internal use in C++17 + template<typename _Tp, typename _Alloc, typename... _Args> constexpr auto __uses_alloc_args(const _Alloc& __a, _Args&&... __args) noexcept { - if constexpr (uses_allocator_v<remove_cv_t<_Tp>, _Alloc>) + if constexpr (!__is_pair<_Tp>::value) { - if constexpr (is_constructible_v<_Tp, allocator_arg_t, - const _Alloc&, _Args...>) + if constexpr (uses_allocator_v<remove_cv_t<_Tp>, _Alloc>) { - return tuple<allocator_arg_t, const _Alloc&, _Args&&...>( - allocator_arg, __a, std::forward<_Args>(__args)...); + if constexpr (is_constructible_v<_Tp, allocator_arg_t, + const _Alloc&, _Args...>) + { + return tuple<allocator_arg_t, const _Alloc&, _Args&&...>( + allocator_arg, __a, std::forward<_Args>(__args)...); + } + else + { + static_assert( + is_constructible_v<_Tp, _Args..., const _Alloc&>); + + return tuple<_Args&&..., const _Alloc&>( + std::forward<_Args>(__args)..., __a); + } } else { - static_assert(is_constructible_v<_Tp, _Args..., const _Alloc&>); + static_assert(is_constructible_v<_Tp, _Args...>); - return tuple<_Args&&..., const _Alloc&>( - std::forward<_Args>(__args)..., __a); + return tuple<_Args&&...>(std::forward<_Args>(__args)...); } } else { - static_assert(is_constructible_v<_Tp, _Args...>); - - return tuple<_Args&&...>(std::forward<_Args>(__args)...); - } - } - -#if __cpp_concepts - template<typename _Tp> - concept bool _Std_pair = __is_pair<_Tp>::value; -#endif - -// This is a temporary workaround until -fconcepts is implied by -std=gnu++2a -#if __cpp_concepts -# define _GLIBCXX_STD_PAIR_CONSTRAINT(T) _Std_pair T -# define _GLIBCXX_STD_PAIR_CONSTRAINT_(T) _Std_pair T -#else -# define _GLIBCXX_STD_PAIR_CONSTRAINT(T) \ - typename T, typename __ = _Require<__is_pair<T>> -# define _GLIBCXX_STD_PAIR_CONSTRAINT_(T) typename T, typename -#endif - - template<typename _Tp, -#if ! __cpp_concepts - typename __ = _Require<__not_<__is_pair<_Tp>>>, -#endif - typename _Alloc, typename... _Args> - constexpr auto - uses_allocator_construction_args(const _Alloc& __a, - _Args&&... __args) noexcept -#if __cpp_concepts - requires ! _Std_pair<_Tp> -#endif - { - return std::__uses_alloc_args<_Tp>(__a, std::forward<_Args>(__args)...); - } - - template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc, - typename _Tuple1, typename _Tuple2> - constexpr auto - uses_allocator_construction_args(const _Alloc& __a, piecewise_construct_t, - _Tuple1&& __x, _Tuple2&& __y) noexcept; - - template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc> - constexpr auto - uses_allocator_construction_args(const _Alloc&) noexcept; - - template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc, - typename _Up, typename _Vp> - constexpr auto - uses_allocator_construction_args(const _Alloc&, _Up&&, _Vp&&) noexcept; - - template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc, - typename _Up, typename _Vp> - constexpr auto - uses_allocator_construction_args(const _Alloc&, - const pair<_Up, _Vp>&) noexcept; - - template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc, - typename _Up, typename _Vp> - constexpr auto - uses_allocator_construction_args(const _Alloc&, pair<_Up, _Vp>&&) noexcept; + static_assert(sizeof...(__args) <= 3); - template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc, - typename _Tuple1, typename _Tuple2> - constexpr auto - uses_allocator_construction_args(const _Alloc& __a, piecewise_construct_t, - _Tuple1&& __x, _Tuple2&& __y) noexcept - { - using _Tp1 = typename _Tp::first_type; - using _Tp2 = typename _Tp::second_type; - - return std::make_tuple(piecewise_construct, - std::apply([&__a](auto&&... __args1) { - return std::uses_allocator_construction_args<_Tp1>( - __a, std::forward<decltype(__args1)>(__args1)...); - }, std::forward<_Tuple1>(__x)), - std::apply([&__a](auto&&... __args2) { - return std::uses_allocator_construction_args<_Tp2>( - __a, std::forward<decltype(__args2)>(__args2)...); - }, std::forward<_Tuple2>(__y))); - } - - template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc> - constexpr auto - uses_allocator_construction_args(const _Alloc& __a) noexcept - { - using _Tp1 = typename _Tp::first_type; - using _Tp2 = typename _Tp::second_type; - - return std::make_tuple(piecewise_construct, - std::uses_allocator_construction_args<_Tp1>(__a), - std::uses_allocator_construction_args<_Tp2>(__a)); - } + using _Tp1 = typename _Tp::first_type; + using _Tp2 = typename _Tp::second_type; - template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc, - typename _Up, typename _Vp> - constexpr auto - uses_allocator_construction_args(const _Alloc& __a, _Up&& __u, _Vp&& __v) - noexcept - { - using _Tp1 = typename _Tp::first_type; - using _Tp2 = typename _Tp::second_type; - - return std::make_tuple(piecewise_construct, - std::uses_allocator_construction_args<_Tp1>(__a, - std::forward<_Up>(__u)), - std::uses_allocator_construction_args<_Tp2>(__a, - std::forward<_Vp>(__v))); - } - - template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc, - typename _Up, typename _Vp> - constexpr auto - uses_allocator_construction_args(const _Alloc& __a, - const pair<_Up, _Vp>& __pr) noexcept - { - using _Tp1 = typename _Tp::first_type; - using _Tp2 = typename _Tp::second_type; - - return std::make_tuple(piecewise_construct, - std::uses_allocator_construction_args<_Tp1>(__a, __pr.first), - std::uses_allocator_construction_args<_Tp2>(__a, __pr.second)); + if constexpr (sizeof...(__args) == 0) + { + return std::make_tuple(piecewise_construct, + std::__uses_alloc_args<_Tp1>(__a), + std::__uses_alloc_args<_Tp2>(__a)); + } + else if constexpr (sizeof...(__args) == 1) + { + return std::make_tuple(piecewise_construct, + std::__uses_alloc_args<_Tp1>(__a, + std::forward<_Args>(__args).first...), + std::__uses_alloc_args<_Tp2>(__a, + std::forward<_Args>(__args).second...)); + } + else if constexpr (sizeof...(__args) == 2) + { + return [&](auto&& __arg1, auto&& __arg2) + { + return std::make_tuple(piecewise_construct, + std::__uses_alloc_args<_Tp1>(__a, + std::forward<decltype(__arg1)>(__arg1)), + std::__uses_alloc_args<_Tp2>(__a, + std::forward<decltype(__arg2)>(__arg2))); + }(std::forward<_Args>(__args)...); + } + else if constexpr (sizeof...(__args) == 3) + { + return [&](piecewise_construct_t, auto&& __arg1, auto&& __arg2) + { + return std::make_tuple(piecewise_construct, + std::apply([&](auto&&... __args1) { + return std::__uses_alloc_args<_Tp1>(__a, + std::forward<decltype(__args1)>(__args1)...); + }, std::forward<decltype(__arg1)>(__arg1)), + std::apply([&](auto&&... __args2) { + return std::__uses_alloc_args<_Tp2>(__a, + std::forward<decltype(__args2)>(__args2)...); + }, std::forward<decltype(__arg2)>(__arg2))); + }(std::forward<_Args>(__args)...); + } + } } - template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc, - typename _Up, typename _Vp> +#if __cplusplus > 201703L + template<typename _Tp, typename _Alloc, typename... _Args> constexpr auto - uses_allocator_construction_args(const _Alloc& __a, - pair<_Up, _Vp>&& __pr) noexcept + uses_allocator_construction_args(const _Alloc& __a, _Args&&... __args) { - using _Tp1 = typename _Tp::first_type; - using _Tp2 = typename _Tp::second_type; - - return std::make_tuple(piecewise_construct, - std::uses_allocator_construction_args<_Tp1>(__a, - std::move(__pr).first), - std::uses_allocator_construction_args<_Tp2>(__a, - std::move(__pr).second)); + return std::__uses_alloc_args<_Tp>(__a, + std::forward<_Args>(__args)...); } template<typename _Tp, typename _Alloc, typename... _Args> @@ -356,8 +283,8 @@ get_pointer_safety() noexcept { return pointer_safety::relaxed; } return ::new(__vp) _Tp(std::make_obj_using_allocator<_Tp>(__a, std::forward<_Args>(__args)...)); } - #endif // C++2a +#endif // C++17 _GLIBCXX_END_NAMESPACE_VERSION } // namespace diff --git a/libstdc++-v3/include/std/memory_resource b/libstdc++-v3/include/std/memory_resource index a212bccc9b1..3ea6990ecae 100644 --- a/libstdc++-v3/include/std/memory_resource +++ b/libstdc++-v3/include/std/memory_resource @@ -124,14 +124,6 @@ namespace pmr template<typename _Tp> class polymorphic_allocator { - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 2975. Missing case for pair construction in polymorphic allocators - template<typename _Up> - struct __not_pair { using type = void; }; - - template<typename _Up1, typename _Up2> - struct __not_pair<pair<_Up1, _Up2>> { }; - public: using value_type = _Tp; @@ -170,89 +162,15 @@ namespace pmr __attribute__((__nonnull__)) { _M_resource->deallocate(__p, __n * sizeof(_Tp), alignof(_Tp)); } -#if __cplusplus <= 201703L - template<typename _Tp1, typename... _Args> - __attribute__((__nonnull__)) - typename __not_pair<_Tp1>::type - construct(_Tp1* __p, _Args&&... __args) - { - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 2969. polymorphic_allocator::construct() shouldn't pass resource() - using __use_tag - = std::__uses_alloc_t<_Tp1, polymorphic_allocator, _Args...>; - if constexpr (is_base_of_v<__uses_alloc0, __use_tag>) - ::new(__p) _Tp1(std::forward<_Args>(__args)...); - else if constexpr (is_base_of_v<__uses_alloc1_, __use_tag>) - ::new(__p) _Tp1(allocator_arg, *this, - std::forward<_Args>(__args)...); - else - ::new(__p) _Tp1(std::forward<_Args>(__args)..., *this); - } - - template<typename _Tp1, typename _Tp2, - typename... _Args1, typename... _Args2> - __attribute__((__nonnull__)) - void - construct(pair<_Tp1, _Tp2>* __p, piecewise_construct_t, - tuple<_Args1...> __x, tuple<_Args2...> __y) - { - auto __x_tag = - __use_alloc<_Tp1, polymorphic_allocator, _Args1...>(*this); - auto __y_tag = - __use_alloc<_Tp2, polymorphic_allocator, _Args2...>(*this); - index_sequence_for<_Args1...> __x_i; - index_sequence_for<_Args2...> __y_i; - - ::new(__p) pair<_Tp1, _Tp2>(piecewise_construct, - _S_construct_p(__x_tag, __x_i, __x), - _S_construct_p(__y_tag, __y_i, __y)); - } - - template<typename _Tp1, typename _Tp2> - __attribute__((__nonnull__)) - void - construct(pair<_Tp1, _Tp2>* __p) - { this->construct(__p, piecewise_construct, tuple<>(), tuple<>()); } - - template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp> - __attribute__((__nonnull__)) - void - construct(pair<_Tp1, _Tp2>* __p, _Up&& __x, _Vp&& __y) - { - this->construct(__p, piecewise_construct, - forward_as_tuple(std::forward<_Up>(__x)), - forward_as_tuple(std::forward<_Vp>(__y))); - } - - template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp> - __attribute__((__nonnull__)) - void - construct(pair<_Tp1, _Tp2>* __p, const std::pair<_Up, _Vp>& __pr) - { - this->construct(__p, piecewise_construct, - forward_as_tuple(__pr.first), - forward_as_tuple(__pr.second)); - } - - template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp> - __attribute__((__nonnull__)) - void - construct(pair<_Tp1, _Tp2>* __p, pair<_Up, _Vp>&& __pr) - { - this->construct(__p, piecewise_construct, - forward_as_tuple(std::forward<_Up>(__pr.first)), - forward_as_tuple(std::forward<_Vp>(__pr.second))); - } -#else template<typename _Tp1, typename... _Args> __attribute__((__nonnull__)) void construct(_Tp1* __p, _Args&&... __args) { - std::uninitialized_construct_using_allocator(__p, *this, - std::forward<_Args>(__args)...); + ::new ((void*)__p) _Tp1(std::make_from_tuple<_Tp1>( + std::__uses_alloc_args<_Tp1>(*this, + std::forward<_Args>(__args)...))); } -#endif template<typename _Up> __attribute__((__nonnull__)) @@ -270,30 +188,6 @@ namespace pmr { return _M_resource; } private: - using __uses_alloc1_ = __uses_alloc1<polymorphic_allocator>; - using __uses_alloc2_ = __uses_alloc2<polymorphic_allocator>; - - template<typename _Ind, typename... _Args> - static tuple<_Args&&...> - _S_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t) - { return std::move(__t); } - - template<size_t... _Ind, typename... _Args> - static tuple<allocator_arg_t, polymorphic_allocator, _Args&&...> - _S_construct_p(__uses_alloc1_ __ua, index_sequence<_Ind...>, - tuple<_Args...>& __t) - { - return { - allocator_arg, *__ua._M_a, std::get<_Ind>(std::move(__t))... - }; - } - - template<size_t... _Ind, typename... _Args> - static tuple<_Args&&..., polymorphic_allocator> - _S_construct_p(__uses_alloc2_ __ua, index_sequence<_Ind...>, - tuple<_Args...>& __t) - { return { std::get<_Ind>(std::move(__t))..., *__ua._M_a }; } - memory_resource* _M_resource; };