【问题标题】:How to perfect forward a value returned from a function while returning a second value?如何在返回第二个值的同时完善从函数返回的值?
【发布时间】:2020-08-09 11:17:04
【问题描述】:

我想创建一个benchmark 函数,给定一个可调用对象及其参数,该函数将为给定可调用对象的执行计时并返回测量的std::chrono::duration<...> 以及调用可调用对象返回的值。

我在完美转发调用返回的值时遇到问题。目前我返回调用返回的值,并使用引用参数返回持续时间:

using bench_clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady, 
                      std::chrono::high_resolution_clock, std::chrono::steady_clock>;

decltype(auto) benchmark(
   bench_clock::duration& duration, auto&& func, auto&&... args)
{
   auto begin{ bench_clock::now() };
   decltype(auto) result{ 
      std::invoke(
         std::forward<decltype(func)>(func), 
         std::forward<decltype(args)>(args)...) 
   };
   duration = bench_clock::now() - begin;
   return result;
}

据我所知,这完美地转发了调用返回的值。

我更愿意以传统方式返回持续时间,例如,使用std::tuple 虽然我不确定如何完美地转发返回的值。

我的猜测是像这样使用std::invoke_result_t

using bench_clock = std::conditional_t<
   std::chrono::high_resolution_clock::is_steady, 
   std::chrono::high_resolution_clock, std::chrono::steady_clock>;

auto benchmark(auto&& func, auto&&... args)
{
   auto begin{ bench_clock::now() };
   decltype(auto) result{
      std::invoke(
         std::forward<decltype(func)>(func),
         std::forward<decltype(args)>(args)...) 
   };
   auto duration{ bench_clock::now() - begin };
   return std::tuple<std::invoke_result_t<decltype(func), decltype(args)...>, bench_clock::duration>{std::forward<decltype(result)>(result), duration};
   //------------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ is this needed?
}

我不确定这种方法是否正确完美地向前推进。我也不知道在std::tuple构造函数中是否需要使用std::forward

还有一个问题,如果invocable返回void,则无法使用元组,因为std::tuple&lt;void&gt;无法实例化。

我不知道如何解决这个问题。

【问题讨论】:

  • 不是std::forward() for result,而是std::move()
  • 对于void的问题...你可以使用C++17还是只使用C++11/C++14?
  • result 是一个左值,不能完美转发。充其量它可以移动到元组中,但不会就地构建。如果是重对象,最好用结果构造元组,然后分配duration部分,希望NRVO发生。

标签: c++ templates return c++17 perfect-forwarding


【解决方案1】:

是的,你是对的。当您在benchmark 函数中调用函数func 时,您需要处理void 返回的情况。

由于您使用过std::invoke_result_tstd::conditional_t,我假设您可以访问。然后这可以通过if constexpr 检查轻松解决。

#include <iostream>
#include <chrono>
#include <tuple>
#include <type_traits>  // std::is_same_v,  std::invoke_result_t
#include <functional>   // std::invoke
#include <utility>      // std::forward, std::move

using namespace std::chrono;
using bench_clock = std::conditional_t<
   high_resolution_clock::is_steady, high_resolution_clock, steady_clock>;


template<typename Func, typename... Args>
decltype(auto) benchmark(Func&& func, Args&&... args)
{
   auto begin{ bench_clock::now() };
   if constexpr (std::is_same_v<void, std::invoke_result_t<Func, Args...>>)
   {
       // can not have void result: therefore just invoke the func!
      std::invoke(std::forward<Func>(func), std::forward<Args>(args)...); 
      return bench_clock::now() - begin; // only the time duration will be returned!
   }
   else
   {
      // all other cases return the tuple of result-duration!
      decltype(auto) result{ std::invoke(std::forward<Func>(func), std::forward<Args>(args)...) };
      const auto duration{ bench_clock::now() - begin };
      return std::make_tuple(std::move(result), duration);
   }
}

(See a Demo Online Live)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-08
    • 1970-01-01
    • 2013-02-10
    • 2018-02-19
    • 2018-02-01
    相关资源
    最近更新 更多