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

Nik*_*kos 10 c++ function-pointers callable c++17

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

Bar*_*rry 13

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

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

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)
Run Code Online (Sandbox Code Playgroud)

特别考虑的区别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)
Run Code Online (Sandbox Code Playgroud)


Nic*_*las 5

您使用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>>>{});
}
Run Code Online (Sandbox Code Playgroud)

当然,这不是世界上最难编写的代码,但也不是完全无关紧要的。特别是如果您没有index_sequence元编程技巧。

2:因为通过拆开a的元素来调用函数tuple非常有用。它支持的基本操作是打包一组参数,传递设置的参数,然后使用这些参数调用函数的能力。从技术上讲,我们已经可以使用单个参数执行此操作(通过传递值),但是通过apply,您可以使用多个参数执行此操作。

它还允许您执行元编程技巧,例如以元编程的方式在语言之间进行编组。您在这样的系统上注册了一个功能,该系统将获得功能的签名(以及功能本身)。该签名用于通过元编程来封送数据。

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

一旦tuple<Params>建成,您使用std::apply调用函数。您真的不能用做到这一点invoke

3:您不想让每个人都将参数贴入一个tuple公正的表述中,以便能够执行的等效功能invoke

4:您需要确定invokeing带有a的函数tupleapplying拆包之间的区别tuple。毕竟,如果您要编写一个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 tuples 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.

But invoke is a simple function for simple needs. The same goes for apply.