繁体   English   中英

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

[英]What is the difference between std::invoke and std::apply?

它们都被用作调用函数,成员函数和通常可调用的任何东西的通用方法。 从cppreference我看到的唯一真正的区别是,在std::invoke ,函数参数(无论它们是多少)都forward给函数,而在std::apply ,参数作为tuple传递。 这真的是唯一的区别吗? 为什么他们会创建一个单独的函数来处理tuple

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

因为你真的需要两个选项,因为他们做了不同的事情。 考虑:

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) ,这确实。 你有时需要两者,需要以某种方式表达。


你是对的, 通常这些是非常密切相关的操作。 实际上,Matt Calabrese正在编写一个名为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)

你使用std::apply因为:

1:实现apply ,即使你有权访问std::invoke也是一件很痛苦的事。 将元组转换为参数包并非易事。 apply的实现看起来像这样(来自cppref):

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的元素来调用函数是非常有用的。 它支持的基本操作是能够打包一组参数,传递该集合,然后使用这些参数调用函数。 我们在技术上已经能够使用单个参数(通过传递值)来执行此操作,但通过apply ,您可以使用多个参数执行此操作。

它还允许您执行元编程技巧,例如以编程方式在语言之间进行编组。 您可以在这样的系统中注册一个函数,该函数具有函数的签名(以及函数本身)。 该签名用于通过元编程来编组数据。

当另一种语言调用您的函数时,元程序生成的函数遍历参数类型列表,并根据这些类型从另一种语言中提取值。 它将它们提取出来的是什么? 某种保存值的数据结构。 而且由于元编程不能(轻松地)构建struct/class ,而是构建一个tuple (实际上,支持这样的元编程是为什么tuple存在的80%)。

一旦构建了tuple<Params> ,就可以使用std::apply来调用该函数。 你不能真的用invoke做到这一点。

3:您不希望每个人都将参数粘贴到tuple ,以便能够执行相当于invoke

4:你需要在invoke一个带tuple的函数和apply解包tuple之间建立区别。 毕竟,如果你正在编写一个模板函数来执行用户指定的参数invoke ,那么如果用户恰好提供了一个tuple作为参数并且你的invoke函数解压缩它就会很糟糕。

您可以使用其他方法来区分案例,但具有不同的功能对于简单案例来说是一个合适的解决方案。 如果你正在写一个更广义的apply风格的功能,要能够解开tuple除了s到传递其他参数或解开多个元组到参数列表(或它们的组合),你会希望有一个特殊的super_invoke可以处理它。

但是invoke是一个简单需要的简单函数。 apply同样apply

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM