C++ 17 std::make_from_tuple

C++ 17 std::make_from_tuple

std::make_from_tuple 可以理解成 std::apply 的“构造版本”。
它会把 tuple 中的元素拆开,然后用这些元素去构造一个目标对象。

1. 先看一个问题

假设有这样一个类:

1
2
3
4
5
6
7
8
9
class Person
{
public:
Person(std::string n, int a) : name(std::move(n)), age(a) {}

private:
std::string name;
int age;
};

如果我们有一个 tuple:

1
auto t = std::make_tuple(std::string("Tom"), 20);

那怎么直接用它来构造 Person 呢?

C++17 之前通常得自己展开参数,或者借助模板技巧。
现在可以直接使用 std::make_from_tuple<Person>(t)

2. 基本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <tuple>
#include <string>
#include <iostream>
using namespace std;

class Person
{
public:
Person(string n, int a) : name(move(n)), age(a) {}

void print() const
{
cout << name << ", " << age << endl;
}

private:
string name;
int age;
};

int main()
{
auto t = make_tuple(string("Tom"), 20);
auto p = make_from_tuple<Person>(t);
p.print();
return 0;
}

3. 它和 std::apply 的关系

两者很像:

  • std::apply:把 tuple 拆开,调用一个函数
  • std::make_from_tuple:把 tuple 拆开,调用一个构造函数

所以如果你已经理解了 apply,那这个接口基本就很好懂了。

4. 适合使用的场景

4.1 参数打包后统一构造对象

有些工厂逻辑会先收集参数,再在某个阶段统一创建对象。

4.2 泛型代码

如果你写的是通用工厂、注册器、反射辅助工具,这个接口会很方便。

4.3 和 tuple 生态配合

当项目里大量用 tuple 传递构造参数时,make_from_tuple 会比较自然。

5. 一个小例子

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

struct Point
{
Point(int x, int y) : x(x), y(y) {}
int x;
int y;
};

int main()
{
auto args = make_tuple(3, 4);
auto pt = make_from_tuple<Point>(args);
cout << pt.x << ", " << pt.y << endl;
return 0;
}

6. 使用时要注意的点

6.1 tuple 元素顺序要和构造函数匹配

这是最基本的要求。

6.2 元素类型也必须匹配

如果构造函数接受 (string, int),那 tuple 也应该能按这个顺序正确传参。

6.3 它不是万能工厂

如果对象创建逻辑本身很复杂,make_from_tuple 只能解决“参数展开构造”这一层问题。

总结

std::make_from_tuple 是一个很小但很顺手的工具。
它把 tuple 到对象构造这一步直接打通了,尤其适合和 applytuple、泛型工厂代码一起使用。项目里如果开始有“参数先打包、后统一构造”的需求,它会省掉不少展开代码。