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.


Reply via email to