Message ID | 20190301135035.GA15423@redhat.com |
---|---|
State | New |
Headers | show |
Series | C++2a Utility functions to implement uses-allocator construction (P0591R4) | expand |
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; };
On 01/03/19 14:06 +0000, Jonathan Wakely wrote: >+ 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) >+ { If we want to ensure we only accept std::pair and not other pair-like types, then this branch would need: static_assert((__is_pair<_Args>::value && ...)); >+ 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)
On 01/03/19 14:06 +0000, Jonathan Wakely wrote: >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 ... [...] >+ 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)...); >+ } I tried replacing this lambda with: using _Targs = tuple<_Args&&...>; _Targs __targs{std::forward<_Args>(__args)...}; using _Args_0 = tuple_element_t<0, _Targs>; using _Args_1 = tuple_element_t<1, _Targs>; return std::make_tuple(piecewise_construct, std::__uses_alloc_args<_Tp1>(__a, std::forward<_Args_0>(std::get<0>(__targs))), std::__uses_alloc_args<_Tp2>(__a, std::forward<_Args_1>(std::get<1>(__targs)))); And similarly for the sizeof...(__args))==3 case. Which seems more straightforward, unfortunately it compiles measurably slower, using more memory. The optimized code is the same size, but unoptimized the lambda version is a bit smaller. The current code on trunk compiles fastest, by quite a big margin. That surprises me as I thought a single function using if-constexpr would outperform several overloads constrained via SFINAE. Being able to use __uses_alloc_args in C++17 might be worth the extra compile-time cost though. I'll keep thinking about it.
On 04/03/19 09:14 +0000, Jonathan Wakely wrote: >On 01/03/19 14:06 +0000, Jonathan Wakely wrote: >>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 ... > >[...] > >>+ 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)...); >>+ } > >I tried replacing this lambda with: > > using _Targs = tuple<_Args&&...>; > _Targs __targs{std::forward<_Args>(__args)...}; > > using _Args_0 = tuple_element_t<0, _Targs>; > using _Args_1 = tuple_element_t<1, _Targs>; > > return std::make_tuple(piecewise_construct, > std::__uses_alloc_args<_Tp1>(__a, > std::forward<_Args_0>(std::get<0>(__targs))), > std::__uses_alloc_args<_Tp2>(__a, > std::forward<_Args_1>(std::get<1>(__targs)))); > >And similarly for the sizeof...(__args))==3 case. Which seems more >straightforward, unfortunately it compiles measurably slower, using >more memory. The optimized code is the same size, but unoptimized the >lambda version is a bit smaller. > >The current code on trunk compiles fastest, by quite a big margin. >That surprises me as I thought a single function using if-constexpr >would outperform several overloads constrained via SFINAE. > >Being able to use __uses_alloc_args in C++17 might be worth the extra >compile-time cost though. I'll keep thinking about it. In case anybody wants to try it out, here's the complete patch (relative to r269312 on trunk) using std::get<N> to extract elements from the pack, instead of using lambdas. diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory index 00a85eef25e..d96dd02b6df 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,174 +176,111 @@ 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...>); + static_assert(sizeof...(__args) <= 3); - return tuple<_Args&&...>(std::forward<_Args>(__args)...); - } - } + using _Tp1 = typename _Tp::first_type; + using _Tp2 = typename _Tp::second_type; -#if __cpp_concepts - template<typename _Tp> - concept bool _Std_pair = __is_pair<_Tp>::value; -#endif + using _Targs = tuple<_Args&&...>; + _Targs __targs{std::forward<_Args>(__args)...}; -// 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> + 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) + { + using _Args_0 = tuple_element_t<0, _Targs>; +#ifdef __STRICT_ANSI__ + static_assert(__is_pair<__remove_cvref_t<_Args_0>>::value); #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; - 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)); - } - - 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)); + return std::make_tuple(piecewise_construct, + std::__uses_alloc_args<_Tp1>(__a, + std::forward<_Args_0>(std::get<0>(__targs)).first), + std::__uses_alloc_args<_Tp2>(__a, + std::forward<_Args_0>(std::get<0>(__targs)).second)); + } + else if constexpr (sizeof...(__args) == 2) + { + using _Args_0 = tuple_element_t<0, _Targs>; + using _Args_1 = tuple_element_t<1, _Targs>; + + return std::make_tuple(piecewise_construct, + std::__uses_alloc_args<_Tp1>(__a, + std::forward<_Args_0>(std::get<0>(__targs))), + std::__uses_alloc_args<_Tp2>(__a, + std::forward<_Args_1>(std::get<1>(__targs)))); + } + else if constexpr (sizeof...(__args) == 3) + { + using _Args_0 = __remove_cvref_t<tuple_element_t<0, _Targs>>; + static_assert(is_same_v<_Args_0, piecewise_construct_t>); + using _Args_1 = tuple_element_t<1, _Targs>; + using _Args_2 = tuple_element_t<2, _Targs>; + + return std::make_tuple(piecewise_construct, + std::apply([&](auto&&... __args1) { + return std::__uses_alloc_args<_Tp1>(__a, + std::forward<decltype(__args1)>(__args1)...); + }, std::forward<_Args_1>(std::get<1>(__targs))), + std::apply([&](auto&&... __args2) { + return std::__uses_alloc_args<_Tp2>(__a, + std::forward<decltype(__args2)>(__args2)...); + }, std::forward<_Args_2>(std::get<2>(__targs)))); + } + } } - 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> inline _Tp make_obj_using_allocator(const _Alloc& __a, _Args&&... __args) { - return std::make_from_tuple<_Tp>(uses_allocator_construction_args<_Tp>( + return std::make_from_tuple<_Tp>(__uses_alloc_args<_Tp>( __a, std::forward<_Args>(__args)...)); } @@ -356,8 +293,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; };
diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory index ff9add9cef2..00a85eef25e 100644 --- a/libstdc++-v3/include/std/memory +++ b/libstdc++-v3/include/std/memory @@ -91,6 +91,8 @@ #include <cstdint> #if __cplusplus > 201703L # include <bit> // for ispow2 +# include <new> // for placement operator new +# include <tuple> // for tuple, make_tuple, make_from_tuple #endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -166,6 +168,197 @@ get_pointer_safety() noexcept { return pointer_safety::relaxed; } } #endif // C++2a +#if __cplusplus > 201703L + template<typename _Tp> + struct __is_pair : false_type { }; + template<typename _Tp, typename _Up> + struct __is_pair<pair<_Tp, _Up>> : true_type { }; + 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> + constexpr auto + __uses_alloc_args(const _Alloc& __a, _Args&&... __args) noexcept + { + if constexpr (uses_allocator_v<remove_cv_t<_Tp>, _Alloc>) + { + 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...>); + + 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; + + 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)); + } + + 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)); + } + + template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc, + typename _Up, typename _Vp> + constexpr auto + uses_allocator_construction_args(const _Alloc& __a, + 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, + std::move(__pr).first), + std::uses_allocator_construction_args<_Tp2>(__a, + std::move(__pr).second)); + } + + template<typename _Tp, typename _Alloc, typename... _Args> + inline _Tp + make_obj_using_allocator(const _Alloc& __a, _Args&&... __args) + { + return std::make_from_tuple<_Tp>(uses_allocator_construction_args<_Tp>( + __a, std::forward<_Args>(__args)...)); + } + + template<typename _Tp, typename _Alloc, typename... _Args> + inline _Tp* + uninitialized_construct_using_allocator(_Tp* __p, const _Alloc& __a, + _Args&&... __args) + { + void* __vp = const_cast<void*>(static_cast<const volatile void*>(__p)); + return ::new(__vp) _Tp(std::make_obj_using_allocator<_Tp>(__a, + std::forward<_Args>(__args)...)); + } + +#endif // C++2a + _GLIBCXX_END_NAMESPACE_VERSION } // namespace #endif // C++11 diff --git a/libstdc++-v3/include/std/memory_resource b/libstdc++-v3/include/std/memory_resource index 93b2ebd9759..a212bccc9b1 100644 --- a/libstdc++-v3/include/std/memory_resource +++ b/libstdc++-v3/include/std/memory_resource @@ -170,6 +170,7 @@ 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 @@ -242,6 +243,16 @@ namespace pmr 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)...); + } +#endif template<typename _Up> __attribute__((__nonnull__)) diff --git a/libstdc++-v3/testsuite/20_util/polymorphic_allocator/construct_c++2a.cc b/libstdc++-v3/testsuite/20_util/polymorphic_allocator/construct_c++2a.cc new file mode 100644 index 00000000000..9048ca196ff --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/polymorphic_allocator/construct_c++2a.cc @@ -0,0 +1,125 @@ +// Copyright (C) 2016-2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include <memory_resource> +#include <utility> +#include <tuple> + +struct do_not_copy { + do_not_copy() = default; + do_not_copy(const do_not_copy&) { throw 1; } +}; + +void +test01() +{ + struct X { + X(do_not_copy&&) { } + }; + + using pair = std::pair<X, int>; + std::pmr::polymorphic_allocator<pair> a; + auto ptr = a.allocate(1); + a.construct(ptr, std::piecewise_construct, + std::tuple<do_not_copy>{}, std::make_tuple(1)); + a.deallocate(ptr, 1); +} + +void +test02() +{ + struct X { + using allocator_type = std::pmr::polymorphic_allocator<int>; + X(do_not_copy&&, const allocator_type&) { } + }; + + using pair = std::pair<X, int>; + std::pmr::polymorphic_allocator<pair> a; + auto ptr = a.allocate(1); + a.construct(ptr, std::piecewise_construct, + std::tuple<do_not_copy>{}, std::make_tuple(1)); + a.deallocate(ptr, 1); +} + +void +test03() +{ + struct X { + using allocator_type = std::pmr::polymorphic_allocator<int>; + X(std::allocator_arg_t, const allocator_type&, do_not_copy&&) { } + }; + + using pair = std::pair<X, int>; + std::pmr::polymorphic_allocator<pair> a; + auto ptr = a.allocate(1); + a.construct(ptr, std::piecewise_construct, + std::tuple<do_not_copy>{}, std::make_tuple(1)); + a.deallocate(ptr, 1); +} + +void +test04() +{ + struct X + { + using allocator_type = std::pmr::polymorphic_allocator<int>; + X() = default; + X(const X&) { throw 1; } + X(const X&, const allocator_type&) { } + }; + + struct Y + { + using allocator_type = std::pmr::polymorphic_allocator<int>; + Y() = default; + Y(const Y&) = delete; + Y(std::allocator_arg_t, const allocator_type&, const Y&) { } + }; + + using pair_type = std::pair<X, Y>; + std::pmr::polymorphic_allocator<pair_type> a; + auto ptr = a.allocate(1); + /* not const */ pair_type p; + a.construct(ptr, p); // LWG 2975 + a.deallocate(ptr, 1); +} + +void +test05() +{ + struct X { + using allocator_type = std::pmr::polymorphic_allocator<char>; + X(int); + X(int, const allocator_type&) { } + }; + std::pmr::polymorphic_allocator<X> a; + auto ptr = a.allocate(1); + a.construct(ptr, 1); + a.deallocate(ptr, 1); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); +} diff --git a/libstdc++-v3/testsuite/20_util/uses_allocator/make_obj.cc b/libstdc++-v3/testsuite/20_util/uses_allocator/make_obj.cc new file mode 100644 index 00000000000..670c59afb33 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/uses_allocator/make_obj.cc @@ -0,0 +1,403 @@ +// Copyright (C) 2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +using test_allocator = __gnu_test::uneq_allocator<int>; + +struct Arg { }; + +struct A +{ + A() : nargs(0) { } + A(float&) : nargs(1) { } + A(int, void*) : nargs(2) { } + + // These should not be used: + A(const test_allocator& a); + A(float&, const test_allocator& a); + A(int, void*, const test_allocator& a); + + const int nargs; + const int alloc_id = -1; + + // std::uses_allocator<A, test_allocator> should be false: + using allocator_type = void*(); +}; + +struct B +{ + // This means std::uses_allocator<B, test_allocator> is true: + using allocator_type = test_allocator; + + B() : nargs(0) { } + B(float&) : nargs(1) { } + B(int, void*) : nargs(2) { } + + B(std::allocator_arg_t, const test_allocator& a) + : nargs(0), alloc_id(a.get_personality()) { } + B(std::allocator_arg_t, const test_allocator& a, float&) + : nargs(1), alloc_id(a.get_personality()) { } + B(std::allocator_arg_t, const test_allocator& a, int, void*) + : nargs(2), alloc_id(a.get_personality()) { } + B(std::allocator_arg_t, const test_allocator& a, B&& b) + : nargs(b.nargs), alloc_id(a.get_personality()) { } + + // These should not be used: + B(const test_allocator&); + B(float&, const test_allocator&, float&); + B(int, void*, const test_allocator&); + B(const test_allocator&, float&); + B(const test_allocator&, int, void*); + B(B&&); + B(B&&, const test_allocator&); + + const int nargs; + const int alloc_id = -1; +}; + +struct C +{ + C() : nargs(0) { } + C(float&) : nargs(1) { } + C(int, void*) : nargs(2) { } + + C(const test_allocator& a) + : nargs(0), alloc_id(a.get_personality()) { } + C(float&, const test_allocator& a) + : nargs(1), alloc_id(a.get_personality()) { } + C(int, void*, const test_allocator& a) + : nargs(2), alloc_id(a.get_personality()) { } + C(C&& c, const test_allocator& a) + : nargs(c.nargs), alloc_id(a.get_personality()) { } + + C(C&&); + + const int nargs; + const int alloc_id = -1; +}; + +namespace std { + // This means std::uses_allocator<C, test_allocator> is true: + template<> struct uses_allocator<C, test_allocator> : std::true_type { }; +} + +test_allocator alloc1(1); +test_allocator alloc2(2); + +void +test01() +{ + auto i0 = std::make_obj_using_allocator<int>(alloc1, 2); + VERIFY( i0 == 2 ); + + float f = 0.0f; + + auto a0 = std::make_obj_using_allocator<A>(alloc1); + VERIFY( a0.nargs == 0 ); + VERIFY( a0.alloc_id == -1 ); + auto a1 = std::make_obj_using_allocator<A>(alloc1, f); + VERIFY( a1.nargs == 1 ); + VERIFY( a1.alloc_id == -1 ); + auto a2 = std::make_obj_using_allocator<A>(alloc1, 123, nullptr); + VERIFY( a2.nargs == 2 ); + VERIFY( a2.alloc_id == -1 ); + + auto b0 = std::make_obj_using_allocator<B>(alloc1); + VERIFY( b0.nargs == 0 ); + VERIFY( b0.alloc_id == 1 ); + auto b1 = std::make_obj_using_allocator<B>(alloc2, f); + VERIFY( b1.nargs == 1 ); + VERIFY( b1.alloc_id == 2 ); + auto b2 = std::make_obj_using_allocator<B>(alloc1, 123, nullptr); + VERIFY( b2.nargs == 2 ); + VERIFY( b2.alloc_id == 1 ); + + auto c0 = std::make_obj_using_allocator<C>(alloc1); + VERIFY( c0.nargs == 0 ); + VERIFY( c0.alloc_id == 1 ); + auto c1 = std::make_obj_using_allocator<C>(alloc2, f); + VERIFY( c1.nargs == 1 ); + VERIFY( c1.alloc_id == 2 ); + auto c2 = std::make_obj_using_allocator<C>(alloc1, 123, nullptr); + VERIFY( c2.nargs == 2 ); + VERIFY( c2.alloc_id == 1 ); +} + +void +test02() +{ + decltype(auto) b + = std::make_obj_using_allocator<const B>(alloc1, 123, nullptr); + static_assert( std::is_const_v<decltype(b)> ); + VERIFY( b.nargs == 2 ); + VERIFY( b.alloc_id == 1 ); + + decltype(auto) c = std::make_obj_using_allocator<const C>(alloc1); + static_assert( std::is_const_v<decltype(c)> ); + VERIFY( c.nargs == 0 ); + VERIFY( c.alloc_id == 1 ); +} + +void +test03() +{ + B b; + decltype(auto) ref = std::make_obj_using_allocator<B&>(alloc1, b); + static_assert( std::is_same_v<decltype(ref), B&> ); + VERIFY( &ref == &b ); + VERIFY( ref.nargs == 0 ); + VERIFY( ref.alloc_id == -1 ); + const B& cref = std::make_obj_using_allocator<const B&>(alloc1, b); + static_assert( std::is_same_v<decltype(cref), const B&> ); + VERIFY( &cref == &b ); + VERIFY( cref.nargs == 0 ); + VERIFY( cref.alloc_id == -1 ); +} + +void +test04() +{ + struct D + { + D(std::allocator_arg_t) { } + D(std::allocator_arg_t, int) { } + + // These should not be used: + D(std::allocator_arg_t, const test_allocator&); + D(std::allocator_arg_t, const test_allocator&, int); + + ~D() { } + }; + + D d1 = std::make_obj_using_allocator<D>(alloc1, std::allocator_arg); + + struct E + { + using allocator_type = test_allocator; + + E(std::allocator_arg_t, const test_allocator&) { } + E(std::allocator_arg_t, int, const test_allocator&) { } + + // These should not be used: + E(std::allocator_arg_t); + E(std::allocator_arg_t, int); + + ~E() { } + }; + + E e1 = std::make_obj_using_allocator<E>(alloc1, std::allocator_arg); + E e2 = std::make_obj_using_allocator<E>(alloc2, std::allocator_arg, 2); +} + +void +test05() +{ + using std::pair; + std::piecewise_construct_t p; + std::tuple<> t0; + float f = 0.0f; + std::tuple<float&> t1(f); + std::tuple<int, void*> t2{}; + + auto aa00 = std::make_obj_using_allocator<pair<A, A>>(alloc1, p, t0, t0); + VERIFY( aa00.first.nargs == 0 ); + VERIFY( aa00.first.alloc_id == -1 ); + VERIFY( aa00.second.nargs == 0 ); + VERIFY( aa00.second.alloc_id == -1 ); + auto ab00 = std::make_obj_using_allocator<pair<A, B>>(alloc1, p, t0, t0); + VERIFY( ab00.first.nargs == 0 ); + VERIFY( ab00.first.alloc_id == -1 ); + VERIFY( ab00.second.nargs == 0 ); + VERIFY( ab00.second.alloc_id == 1 ); + auto bc00 = std::make_obj_using_allocator<pair<B, C>>(alloc2, p, t0, t0); + VERIFY( bc00.first.nargs == 0 ); + VERIFY( bc00.first.alloc_id == 2 ); + VERIFY( bc00.second.nargs == 0 ); + VERIFY( bc00.second.alloc_id == 2 ); + auto cb00 = std::make_obj_using_allocator<pair<C, B>>(alloc2, p, t0, t0); + VERIFY( cb00.first.nargs == 0 ); + VERIFY( cb00.first.alloc_id == 2 ); + VERIFY( cb00.second.nargs == 0 ); + VERIFY( cb00.second.alloc_id == 2 ); + auto cc00 + = std::make_obj_using_allocator<pair<C, const C>>(alloc1, p, t0, t0); + VERIFY( cc00.first.nargs == 0 ); + VERIFY( cc00.first.alloc_id == 1 ); + VERIFY( cc00.second.nargs == 0 ); + VERIFY( cc00.second.alloc_id == 1 ); + + auto aa21 = std::make_obj_using_allocator<pair<A, A>>(alloc1, p, t2, t1); + VERIFY( aa21.first.nargs == 2 ); + VERIFY( aa21.first.alloc_id == -1 ); + VERIFY( aa21.second.nargs == 1 ); + VERIFY( aa21.second.alloc_id == -1 ); + auto ab21 = std::make_obj_using_allocator<pair<A, B>>(alloc1, p, t2, t1); + VERIFY( ab21.first.nargs == 2 ); + VERIFY( ab21.first.alloc_id == -1 ); + VERIFY( ab21.second.nargs == 1 ); + VERIFY( ab21.second.alloc_id == 1 ); + auto bc11 = std::make_obj_using_allocator<pair<B, C>>(alloc2, p, t1, t1); + VERIFY( bc11.first.nargs == 1 ); + VERIFY( bc11.first.alloc_id == 2 ); + VERIFY( bc11.second.nargs == 1 ); + VERIFY( bc11.second.alloc_id == 2 ); + auto cb12 = std::make_obj_using_allocator<pair<C, B>>(alloc2, p, t1, t2); + VERIFY( cb12.first.nargs == 1 ); + VERIFY( cb12.first.alloc_id == 2 ); + VERIFY( cb12.second.nargs == 2 ); + VERIFY( cb12.second.alloc_id == 2 ); + auto cc22 + = std::make_obj_using_allocator<pair<C, const C>>(alloc1, p, t2, t1); + VERIFY( cc22.first.nargs == 2 ); + VERIFY( cc22.first.alloc_id == 1 ); + VERIFY( cc22.second.nargs == 1 ); + VERIFY( cc22.second.alloc_id == 1 ); +} + +void +test06() +{ + using std::pair; + float f = 0.0f; + + auto aa00 = std::make_obj_using_allocator<pair<A, A>>(alloc1); + VERIFY( aa00.first.nargs == 0 ); + VERIFY( aa00.first.alloc_id == -1 ); + VERIFY( aa00.second.nargs == 0 ); + VERIFY( aa00.second.alloc_id == -1 ); + auto ab00 = std::make_obj_using_allocator<pair<A, B>>(alloc1); + VERIFY( ab00.first.nargs == 0 ); + VERIFY( ab00.first.alloc_id == -1 ); + VERIFY( ab00.second.nargs == 0 ); + VERIFY( ab00.second.alloc_id == 1 ); + auto bc00 = std::make_obj_using_allocator<pair<B, C>>(alloc2); + VERIFY( bc00.first.nargs == 0 ); + VERIFY( bc00.first.alloc_id == 2 ); + VERIFY( bc00.second.nargs == 0 ); + VERIFY( bc00.second.alloc_id == 2 ); + auto cb00 = std::make_obj_using_allocator<pair<C, B>>(alloc2); + VERIFY( cb00.first.nargs == 0 ); + VERIFY( cb00.first.alloc_id == 2 ); + VERIFY( cb00.second.nargs == 0 ); + VERIFY( cb00.second.alloc_id == 2 ); + auto cc00 = std::make_obj_using_allocator<pair<C, const C>>(alloc1); + VERIFY( cc00.first.nargs == 0 ); + VERIFY( cc00.first.alloc_id == 1 ); + VERIFY( cc00.second.nargs == 0 ); + VERIFY( cc00.second.alloc_id == 1 ); + + auto aa11 = std::make_obj_using_allocator<pair<A, A>>(alloc1, f, f); + VERIFY( aa11.first.nargs == 1 ); + VERIFY( aa11.first.alloc_id == -1 ); + VERIFY( aa11.second.nargs == 1 ); + VERIFY( aa11.second.alloc_id == -1 ); + auto aba1 = std::make_obj_using_allocator<pair<A, B>>(alloc1, A{}, f); + VERIFY( aba1.first.nargs == 0 ); + VERIFY( aba1.first.alloc_id == -1 ); + VERIFY( aba1.second.nargs == 1 ); + VERIFY( aba1.second.alloc_id == 1 ); + auto bc11 = std::make_obj_using_allocator<pair<B, C>>(alloc2, f, f); + VERIFY( bc11.first.nargs == 1 ); + VERIFY( bc11.first.alloc_id == 2 ); + VERIFY( bc11.second.nargs == 1 ); + VERIFY( bc11.second.alloc_id == 2 ); + auto cb1b = std::make_obj_using_allocator<pair<C, B>>(alloc2, f, B{}); + VERIFY( cb1b.first.nargs == 1 ); + VERIFY( cb1b.first.alloc_id == 2 ); + VERIFY( cb1b.second.nargs == 0 ); + VERIFY( cb1b.second.alloc_id == 2 ); + auto cccc + = std::make_obj_using_allocator<pair<C, const C>>(alloc1, C{}, C{}); + VERIFY( cccc.first.nargs == 0 ); + VERIFY( cccc.first.alloc_id == 1 ); + VERIFY( cccc.second.nargs == 0 ); + VERIFY( cccc.second.alloc_id == 1 ); + + pair<float&, A> p1a(f, A{}); + pair<float&, float&> p11(f, f); + auto aa1a = std::make_obj_using_allocator<pair<A, A>>(alloc1, p1a); + VERIFY( aa1a.first.nargs == 1 ); + VERIFY( aa1a.first.alloc_id == -1 ); + VERIFY( aa1a.second.nargs == 0 ); + VERIFY( aa1a.second.alloc_id == -1 ); + auto ab11 = std::make_obj_using_allocator<pair<A, B>>(alloc1, p11); + VERIFY( ab11.first.nargs == 1 ); + VERIFY( ab11.first.alloc_id == -1 ); + VERIFY( ab11.second.nargs == 1 ); + VERIFY( ab11.second.alloc_id == 1 ); + auto cb11 = std::make_obj_using_allocator<pair<C, B>>(alloc2, p11); + VERIFY( cb11.first.nargs == 1 ); + VERIFY( cb11.first.alloc_id == 2 ); + VERIFY( cb11.second.nargs == 1 ); + VERIFY( cb11.second.alloc_id == 2 ); + + auto bcbc = std::make_obj_using_allocator<pair<B, C>>(alloc2, pair<B, C>()); + VERIFY( bcbc.first.nargs == 0 ); + VERIFY( bcbc.first.alloc_id == 2 ); + VERIFY( bcbc.second.nargs == 0 ); + VERIFY( bcbc.second.alloc_id == 2 ); + + auto cc11 = std::make_obj_using_allocator<pair<C, B>>(alloc2, std::move(p11)); + VERIFY( cc11.first.nargs == 1 ); + VERIFY( cc11.first.alloc_id == 2 ); + VERIFY( cc11.second.nargs == 1 ); + VERIFY( cc11.second.alloc_id == 2 ); +} + +void +test07() +{ + using nested_pair = std::pair<const std::pair<B, const B>, C>; + auto p = std::make_obj_using_allocator<const nested_pair>(alloc1); + VERIFY( p.first.first.alloc_id == 1 ); + VERIFY( p.first.second.alloc_id == 1 ); + VERIFY( p.second.alloc_id == 1 ); +} + +void +test08() +{ + // LWG DR 3187. + // P0591R4 reverted DR 2586 fixes to scoped_allocator_adaptor::construct() + + struct X { + using allocator_type = std::allocator<X>; + X(std::allocator_arg_t, allocator_type&&) { } + X(const allocator_type&) { } + }; + + std::allocator<X> a; + std::make_obj_using_allocator<X>(a); +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); + test08(); +}