【问题标题】:C++ Convert tuple of homogeneous wrapped type to tuple of raw typeC++ 将同构包装类型的元组转换为原始类型的元组
【发布时间】:2019-01-23 09:18:13
【问题描述】:

我想调用std::apply() 到一个函数;但是,我无法这样做,因为我使用的 std::tuple 目前已包装。例如:

#include <tuple>

template <class T>
struct wrapped
{
    wrapped(T t) : t(t) {}

    T t;
};

template <class T, class ... Args>
struct delay_call
{
    T(*callback)(Args...);
    std::tuple<Args...> params;

    delay_call(T(*callback)(Args...), Args ... params) :
        callback(callback), params(params...)
    {}

    T call()
    {
        return std::apply(callback, params);
    }
};

template <class T, class ... Args>
struct delay_call_with_wrap
{
    T(*callback)(Args...);
    std::tuple<wrapped<Args>...> w_params;

    delay_call_with_wrap(T(*callback)(Args...), wrapped<Args> ... w_params) :
        callback(callback), w_params(w_params...)
    {}

    T call()
    {
        std::tuple<Args...> params; // = w_params.t
        return std::apply(callback, actual_params);
    }
};

float saxpy(float a, float x, float y)
{
    return a * x + y;
}

int main()
{
    float a = 1, x = 2, y = 3;
    delay_call delay_saxpy(saxpy, a, x, y);

    wrapped w_a = 1.f, w_x = 2.f, w_y = 3.f;
    delay_call_with_wrap w_delay_saxpy(saxpy, w_a, w_x, w_y);

    float result = delay_saxpy.call();

    float w_result = w_delay_saxpy.call();
}

delay_call 结构按预期工作;但是,我不确定如何提取每个元组元素的实际值并将其交给std::apply() 执行。

简而言之,对于delay_call_with_wrap::call,我如何将std::tuple&lt;wrapped&lt;Args&gt;...&gt; 转换为std::tuple&lt;Args...&gt;

【问题讨论】:

    标签: c++ c++17 wrapper variadic-templates stdtuple


    【解决方案1】:

    我会完全避免std::apply,并通过使用std::index_sequence 解包元组直接调用callback

    template <std::size_t ...I> T call_low(std::index_sequence<I...>)
    {
        return callback(std::get<I>(w_params).t...);
    }
    
    T call()
    {
        return call_low(std::make_index_sequence<sizeof...(Args)>{});
    }
    

    【讨论】:

      【解决方案2】:

      简而言之,对于delay_call_with_wrap::call,我如何将std::tuple&lt;wrapped&lt;Args&gt;...&gt; 转换为std::tuple&lt;Args...&gt;

      在我看来,最好避免使用旧的std::make_index_sequence/std::index_sequence 方式std::apply()(请参阅 HolyBlackCat 答案)。

      但是,如果你真的想使用std::apply(),你可以第一次调用它来解包元组(得到一个解包值的元组),然后像往常一样调用。

      我的意思是

      T call ()
       {
         auto actual_params = std::apply([](auto ... wt){ return std::make_tuple(wt.t...); },
            w_params);
         return std::apply(callback, actual_params);
      }
      

      或者,在一次调用中,直接

      T call()
       {
         return std::apply(callback,
                   std::apply([](auto ... wt){ return std::make_tuple(wt.t...); },
                      w_params));
       }
      

      这个解决方案是合理的,恕我直言,如果w_param 成员是恒定的,那么您可以一次性计算actual_params 并使其成为static

      【讨论】:

      • 欣赏 std::apply 的解决方案,即使旧的解决方案毕竟更合适。此外,不幸的是,w_params 对于我的用例来说不是恒定的,因此 std::tuple 类型的实际参数无法一次性计算。
      • @MichaelChoi - 我同意:恕我直言,最好的解决方案是 HolyBlackCat 的答案。
      【解决方案3】:

      可能不是在实践中使用的最佳解决方案,但这里有一个使用可变模板化的 lambda 来避免 index_sequence

      template <class T, class ... Args>
      struct delay_call_with_wrap
      {
          T(*callback)(Args...);
          std::tuple<wrapped<Args>...> w_params;
      
          delay_call_with_wrap(T(*callback)(Args...), wrapped<Args> ... w_params) :
              callback(callback), w_params(w_params...)
          {}
      
          T call()
          {
              auto helper = [this] <class ... Args_> (wrapped<Args_>... args)
              {
                  return callback(args.t...);
              };
              return std::apply(helper, w_params);
          }
      };
      

      Demo

      这个想法是只提供一个与std::apply 在这里产生的参数相匹配的函数——它需要采用wrapped&lt;Args&gt;...。从那里开始,在提取包装值的同时扩展包是微不足道的。

      我们使用 lambda 是因为 std::apply 想要一个 Callable,所以我们不能只使用另一个成员函数。好吧,我想我们可以用overload operator()delay_call_with_wrap。这会有点令人困惑,但至少不限于 C++2a(和 missing compiler support),如模板化 lambda。

      【讨论】:

      • 我明白了...我没有意识到 lambda 可以在 C++17 中进行模板化。在我看来,这个解决方案最容易实施。
      • @MichaelChoi 哦,对不起,我错过了 C++17 标签。模板化的 lambda 仅存在于 C++20 中(并且,如图所示,还没有良好的编译器支持)。但如果它们是语言的稳定部分,这将是一个很好的用例。
      猜你喜欢
      • 2020-06-04
      • 1970-01-01
      • 1970-01-01
      • 2017-09-10
      • 1970-01-01
      • 1970-01-01
      • 2020-04-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多