代码之家  ›  专栏  ›  技术社区  ›  KeyC0de

std::invoke和std::apply之间有什么区别?

  •  6
  • KeyC0de  · 技术社区  · 6 年前

    它们都被用作调用函数、成员函数和任何可调用的通用方法。从中我看到的唯一真正的区别是 std::invoke 函数参数(不管多大)是 forward 与函数有关,而 std::apply 参数作为 tuple . 这真的是唯一的区别吗?他们为什么要创建一个单独的函数来处理 元组

    2 回复  |  直到 6 年前
        1
  •  18
  •   Barry    6 年前

    这真的是唯一的区别吗?他们为什么要创建一个单独的函数来处理元组呢?

    int f(int, int);
    int g(tuple<int, int>);
    
    tuple<int, int> tup(1, 2);
    
    invoke(f, 1, 2); // calls f(1, 2)
    invoke(g, tup);  // calls g(tup)
    apply(f, tup);   // also calls f(1, 2)
    

    尤其要考虑 invoke(g, tup) ,是的 tuple ,和 apply(f, tup) ,确实如此。你有时两者都需要,需要以某种方式表达出来。


    你说得对 通常地 这些都是密切相关的行动。事实上,马特·卡拉布雷斯正在写一个名为 Argot 它结合了两种操作,并不是通过调用的函数来区分它们,而是通过修饰参数的方式来区分它们:

    call(f, 1, 2);         // f(1,2)
    call(g, tup);          // g(tup)
    call(f, unpack(tup));  // f(1, 2), similar to python's f(*tup)
    
        2
  •  7
  •   Nicol Bolas    6 年前

    你用 std::apply

    1:实施 apply ,即使您可以访问 std::invoke 这是一个很大的痛苦。将元组转换为参数包不是一个简单的操作。 应用

    namespace detail {
    template <class F, class Tuple, std::size_t... I>
    constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>)
    {
        return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);
    }
    }  // namespace detail
    
    template <class F, class Tuple>
    constexpr decltype(auto) apply(F&& f, Tuple&& t)
    {
        return detail::apply_impl(
            std::forward<F>(f), std::forward<Tuple>(t),
            std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
    }
    

    当然,这不是世界上最难写的代码,但也不是很简单。尤其是如果你不知道 index_sequence

    2:因为通过解包 tuple 是相当有用的。它支持的基本操作是打包一组参数,传递参数集,然后用这些参数调用函数。从技术上讲,我们已经能够使用单个参数(通过传递值)来实现这一点,但是 ,您将获得使用多个参数执行此操作的能力。

    它还允许您使用元编程技巧,比如元编程在语言之间进行编组。在这样一个系统中注册一个函数,这个系统被赋予函数的签名(以及函数本身)。该签名用于通过元编程封送数据。

    当其他语言调用您的函数时,元程序生成的函数遍历参数类型列表,并基于这些类型从其他语言提取值。它把它们提炼成什么?保存值的某种数据结构。由于元编程不能(轻松地)构建 struct/class ,而是构建一个 元组 元组 存在)。

    一旦 tuple<Params> 是建的,你用 标准::应用 调用函数。你真的不能用这个 invoke .

    3:你不想让每个人都把参数粘到一个 只是为了能表现出 援引 .

    4:你需要确定 援引 调用一个函数 ,和 打开包装 元组 . 毕竟,如果您编写的模板函数 援引 在用户指定的参数上,如果用户碰巧提供了一个 元组 作为一个论据 函数将其解包。

    应用 -样式函数,您希望能够在其中解包 元组 super_invoke 那可以应付。

    但是 是满足简单需求的简单函数。同样的道理 .