C++ 17 std::apply

C++ 17 std::apply

std::apply 的作用可以概括成一句话:把 tuple 里的元素拆开,作为函数参数传进去。
它是 tuple、可调用对象和模板工具之间的一座桥。

1. 为什么需要 std::apply?

假设你有一个函数:

1
2
3
4
int add(int a, int b, int c)
{
return a + b + c;
}

同时你手里拿到的是:

1
std::tuple<int, int, int> t{1, 2, 3};

以前如果要调用这个函数,通常得手动写:

1
add(std::get<0>(t), std::get<1>(t), std::get<2>(t));

std::apply 可以直接完成这件事。

2. 基本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <tuple>
#include <iostream>
using namespace std;

int add(int a, int b, int c)
{
return a + b + c;
}

int main()
{
tuple<int, int, int> t{1, 2, 3};
cout << apply(add, t) << endl;
return 0;
}

输出:

1
6

3. 不只适用于普通函数

std::apply 也可以配合 lambda 使用:

1
2
3
4
auto t = std::make_tuple(10, 20);
auto ret = std::apply([](int a, int b) {
return a * b;
}, t);

还可以配合函数对象:

1
2
3
4
5
6
struct Print
{
void operator()(int a, double b) const
{
}
};

4. 一个常见场景

有时我们会把参数先打包成 tuple,后面再统一执行某个调用:

1
2
3
4
5
6
7
8
9
10
11
12
#include <tuple>
#include <string>

void logMessage(int level, const std::string& text)
{
}

int main()
{
auto args = std::make_tuple(2, std::string("hello"));
std::apply(logMessage, args);
}

这种模式在任务调度、延迟调用、泛型封装里很常见。

5. 配合结构化绑定理解

你可以把它理解成另一种“解包 tuple”的方式:

  • 结构化绑定:把 tuple 拆到多个变量里
  • std::apply:把 tuple 拆成函数参数

两者思想上是相通的。

6. 使用时要注意的点

6.1 tuple 元素顺序必须和函数参数顺序对应

如果顺序不对,调用就会失败,或者逻辑错误。

6.2 tuple 中元素类型也必须匹配

它不会帮你自动做复杂的参数重排。

6.3 适合泛型封装场景

如果只是写死的三四个参数,手写调用也不是不行;std::apply 的价值更多体现在通用代码里。

总结

std::apply 是一个很好理解、也很实用的小工具。
它把 tuple 拆成可调用对象的参数列表,在延迟调用、泛型封装和参数转发场景里很顺手。只要你项目里开始频繁接触 tuple,这个接口基本都会遇到。