Preface

偶尔想写一点自己感兴趣的C++语法内容,今天终于得以付诸实施,就从结构化绑定(Structured Binding)开始吧。

Grammar

结构化绑定的语法很简单,大致有如下三种形式:

attr cv-auto ref-qualifier [identifier-list] = expression; // 1
attr cv-auto ref-qualifier [identifier-list] {expression}; // 2
attr cv-auto ref-qualifier [identifier-list] (expression); // 3

其中属性attr和引用运算符ref-qualifier&或者&&)都是可选的,可以不出现;cv-auto是可以带constvolitileauto类型声明,此类型声明也可以是static或者thread_localidentifier-listexpression要绑定到的变量名列表,如果前者中的某个成员出现在后者中则视为错误。

如果类型声明为auto,则expression的值会一一按顺序复制到identifier-list的各个成员中;如果是auto &或者auto &&,则expression中的各个子对象会直接绑定到identifier-list中各个变量上,即引用。

所以有人在C++17发布后讲到,现在的C++越来越像Python了。恐怕这个新语法对这种想法也有不小的贡献。

Examples

首先是绑定数组成员到变量:

int arr = {0, 1, 2};

auto [a, b, c] = arr; // E1
auto& [ar, br, cr] = arr; // E2

这个应该比较好理解,a, b, c是复制了数组中的元素,而ar, br, cr是指向数组各元素的引用,如果可以打印各变量的指针,那么应该可以很容易就验证这个结论。

绑定结构成员到变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
std::unordered_map<std::string, int> get_map()
{
return {
{"James T. Kirk", 1},
{"Spock", 2},
{"Old Bones", 3},
};
}

int main(int argc, char** argv)
{
for (auto&& [k, v] : get_map())
{
std::printf("Name: %s, Value: %d\n", k.c_str(), k);
}

return EXIT_SUCCESS;
}

这里的结构成员不单指struct或者class声明的自定义类型,也包括std::setstd::unordered_map等这些容器,可以把它们看做成员匿名的结构体或者类。

在需要遍历数个同类型变量,且这些变量时相互独立的,也可以使用结构化绑定以简化代码。

#include <filesystem>

namespace fs = std::filesystem;

int main()
{
fs::path p1, p2, p3, p4;

for (auto& p : {p1, p2, p3, p4})
{
// do something...
}
}

此处在for循环中声明了一个局部的tuple,并遍历它。

Reference

  1. 结构化绑定声明