[英]What is the difference between std::invoke and std::apply?
They are both used as a generic method of calling functions, member functions and generally anything that is callable. 它们都被用作调用函数,成员函数和通常可调用的任何东西的通用方法。 From cppreference the only real difference I see is that in
std::invoke
the function parameters (however many they are) are forward
ed to the function, whereas in std::apply
the parameters are passed as a tuple
. 从cppreference我看到的唯一真正的区别是,在
std::invoke
,函数参数(无论它们是多少)都forward
给函数,而在std::apply
,参数作为tuple
传递。 Is this really the only difference? 这真的是唯一的区别吗? Why would they create a separate function just to handle
tuple
s? 为什么他们会创建一个单独的函数来处理
tuple
?
Is this really the only difference?
这真的是唯一的区别吗? Why would they create a separate function just to handle tuples?
为什么他们会创建一个单独的函数来处理元组?
Because you really need both options, since they do different things. 因为你真的需要两个选项,因为他们做了不同的事情。 Consider:
考虑:
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)
Consider especially the difference between invoke(g, tup)
, which does not unpack the tuple
, and apply(f, tup)
, which does. 考虑特别是间差
invoke(g, tup)
其不解压tuple
,和apply(f, tup)
,这确实。 You sometimes need both, that needs to be expressed somehow. 你有时需要两者,需要以某种方式表达。
You're right that generally these are very closely related operations. 你是对的, 通常这些是非常密切相关的操作。 Indeed, Matt Calabrese is writing a library named Argot that combines both operations and you differentiate them not by the function you call but rather by how you decorate the arguments:
实际上,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)
You use std::apply
because: 你使用
std::apply
因为:
1: Implementing apply
, even if you have access to std::invoke
is a big pain. 1:实现
apply
,即使你有权访问std::invoke
也是一件很痛苦的事。 Turning a tuple into a parameter pack is not a trivial operation. 将元组转换为参数包并非易事。
apply
's implementation would look something like this (from cppref): 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>>>{});
}
Sure, it's not the hardest code in the world to write, but it's not exactly trivial either. 当然,这不是世界上最难编写的代码,但它也不是一件容易的事。 Especially if you don't the
index_sequence
metaprogramming trick. 特别是如果你没有
index_sequence
元编程技巧。
2: Because calling a function by unpacking the elements of a tuple
is rather useful. 2:因为通过解包
tuple
的元素来调用函数是非常有用的。 The basic operation it exists to support is the ability to package up a set of arguments, pass that set around, and then call a function with those parameters. 它支持的基本操作是能够打包一组参数,传递该集合,然后使用这些参数调用函数。 We already technically have the ability to do that with a single parameter (by passing the value around), but through
apply
, you gain the ability to do it with multiple parameters. 我们在技术上已经能够使用单个参数(通过传递值)来执行此操作,但通过
apply
,您可以使用多个参数执行此操作。
It also allows you to do metaprogramming tricks, like meta-programmatically doing marshalling between languages. 它还允许您执行元编程技巧,例如以编程方式在语言之间进行编组。 You register a function with such a system, which is given the function's signature (and the function itself).
您可以在这样的系统中注册一个函数,该函数具有函数的签名(以及函数本身)。 That signature is used to marshal the data through metaprogramming.
该签名用于通过元编程来编组数据。
When the other language calls your function, the metaprogram-generated function walks the list of parameter types and extracts value(s) from the other language based on those types. 当另一种语言调用您的函数时,元程序生成的函数遍历参数类型列表,并根据这些类型从另一种语言中提取值。 What does it extract them into?
它将它们提取出来的是什么? Some kind of data structure that holds the values.
某种保存值的数据结构。 And since metaprogramming cannot (easily) build a
struct/class
, you instead build a tuple
(indeed, supporting metaprogramming like this is 80% of why tuple
exists). 而且由于元编程不能(轻松地)构建
struct/class
,而是构建一个tuple
(实际上,支持这样的元编程是为什么tuple
存在的80%)。
Once the tuple<Params>
is built, you use std::apply
to call the function. 一旦构建了
tuple<Params>
,就可以使用std::apply
来调用该函数。 You can't really do that with invoke
. 你不能真的用
invoke
做到这一点。
3: You don't want to make everyone stick parameters into a tuple
just to be able to perform the equivalent of invoke
. 3:您不希望每个人都将参数粘贴到
tuple
,以便能够执行相当于invoke
。
4: You need to establish the difference between invoke
ing a function that takes a tuple
, and apply
ing to unpack the tuple
. 4:你需要在
invoke
一个带tuple
的函数和apply
解包tuple
之间建立区别。 After all, if you're writing a template function that performs invoke
on parameters the user specifies, it'd be terrible if the user just so happened to provide a single tuple
as an argument and your invoke
function unpacked it. 毕竟,如果你正在编写一个模板函数来执行用户指定的参数
invoke
,那么如果用户恰好提供了一个tuple
作为参数并且你的invoke
函数解压缩它就会很糟糕。
You could use other means to differentiate the cases, but having different functions is an adequate solution for the simple case. 您可以使用其他方法来区分案例,但具有不同的功能对于简单案例来说是一个合适的解决方案。 If you were writing a more generalized
apply
-style function, where you want to be able to unpack tuple
s in addition to passing other arguments or unpack multiple tuples into the argument lists (or a combination of these), you would want to have a special super_invoke
that could handle that. 如果你正在写一个更广义的
apply
风格的功能,要能够解开tuple
除了s到传递其他参数或解开多个元组到参数列表(或它们的组合),你会希望有一个特殊的super_invoke
可以处理它。
But invoke
is a simple function for simple needs. 但是
invoke
是一个简单需要的简单函数。 The same goes for apply
. apply
同样apply
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.