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.