一、简介C17 为 C 语言带来了许多功能。本文深入研究其中的三个它们有助于使编码更容易、更简洁、更直观和正确。本文从结构化绑定开始。结构化绑定适用于许多情况本文介绍几种情况可以使代码更简洁、更简单。介绍模板参数推导可以删除习惯键入的模板参数。最后介绍用“选择初始化”更好控制对象范围并允许定义它们所属的值。二、结构化绑定结构化绑定允许一次性定义多个对象其方式比以前的 C 版本更自然。从 C11 到 C17这个概念本身并不新鲜。C17以前始终可以从函数返回多个值并使用std::tie。例如展开代码语言C自动换行AI代码解释std::tuplechar, int, bool mytuple() { char a a; int i 123; bool b true; return std::make_tuple(a, i, b); }这返回三个不同类型的变量。要从 C17 之前调用函数访问这些函数要如下内容代码语言C自动换行AI代码解释char a; int i; bool b; std::tie(a, i, b) mytuple();其中必须在用前定义变量并预先了解类型。但是使用结构化绑定可以简单执行为代码语言C自动换行AI代码解释auto [a, i, b] mytuple();这是一种更好的语法几乎尽可能用auto与现代C样式也一致。那么什么可以与结构化绑定初始化一起使用呢基本上任何复合类型struct、pair和tuple。让我们看看它可能有用的几种情况。2.1、返回复合对象这是一次性将复合类型的各个部分如struct、pair等分配给不同变量并自动分配正确类型的简单方法。例如如果把数据插入到std::map中那么结果是一个std::pair。代码语言C自动换行AI代码解释std::mapchar,int mymap; auto mapret mymap.insert(std::pair(a, 100));可能有人想知道上面的代码为什么没有明确说明配对的类型这就是后面要讲的C17 中的模板参数推导了请接着往下阅读。因此为了确定数据插入是否成功可以从插入方法返回的内容mapret中提取second信息。问题在于读者应该需要查找什么才能确定是否插入成功。但是使用结构化绑定这将变成代码语言C自动换行AI代码解释auto [itelem, success] mymap.insert(std::pair(’a’, 100)); If (!success) { // Insert failure }其中itelem是元素的迭代器success的类型为booltrue用于表示插入成功。变量的类型是从赋值中自动推断出来的——这在阅读代码时更有意义。选择初始化作为本文最后一节的内容这里先睹为快由于 C17 现在具有选择初始化那么我们可以将其编写为代码语言C自动换行AI代码解释if (auto [itelem, success] mymap.insert(std::pair(‘a’, 100)); success) { // Insert success }这个稍后会详细介绍。2.2、遍历复合集合结构化绑定也适用于范围。因此考虑到前面的mymap定义在 C17 之前是使用如下所示的代码对其进行迭代代码语言C自动换行AI代码解释for (const auto entry : mymap) { // Process key as entry.first // Process value as entry.second }或者更明确地说代码语言C自动换行AI代码解释for (const auto entry : mymap) { auto key entry.first; auto value entry.second; // Process entry }但是结构化绑定允许更直接地编写它代码语言C自动换行AI代码解释for (const auto[key, value] : mymap) { // Process entry using key and value }变量key和value的用法比entry.first和entry.second更具指导意义并且不需要额外的变量定义。2.3、直接初始化但是由于结构化绑定可以从tuple、pair等进行初始化我们可以以这种方式进行直接初始化吗是的我们能。比如代码语言C自动换行AI代码解释auto a ‘a’; auto i 123; auto b true;它将变量a的初始值定义为char 类型的ai初始值为int类型的123以及b初始值为bool的类型的true。使用结构化绑定可以写成代码语言C自动换行AI代码解释auto [a, i, b] tuple(‘a’, 123, true); // With no types needed for the tuple!这将变量a、i、b定义得就像使用了上面的单独定义一样。这真的是对之前定义的改进吗已经在一行中完成了本来需要三行才能完成的工作但为什么要这样做呢考虑以下代码代码语言C自动换行AI代码解释{ istringstream iss(head); for (string name; getline(iss, name); ) // Process name }iss和name两者都只在for块内使用但必须在for语句之外和它自己的块内声明以便将范围限制在所需的范围内。这很奇怪因为iss是属于for循环。始终可以初始化相同类型的多个变量。例如代码语言C自动换行AI代码解释for (int i 0, j 100; i 42; i, --j) { // Use i and j }但我们想写但不能写的是代码语言C自动换行AI代码解释for (int i 0, char ch ‘ ‘; i 42; i) { // Does not compile // Use i and ch }使用结构化绑定我们就可以这样编写代码语言C自动换行AI代码解释for (auto[iss, name] pair(istringstream(head), string {}); getline(iss, name); ) { // Process name }和代码语言C自动换行AI代码解释for (auto[i, ch] pair(0U, ‘ ‘); i 42; i) { // The 0U makes i an unsigned int // Use i and ch }这允许根据需要在for语句的范围内定义变量iss和name以及i和ch并且还可以自动确定它们的类型。if和switch语句也是如此它现在在C 17中采用可选的选择初始化见下文。例如代码语言C自动换行AI代码解释if (auto [a, b] myfunc(); a b) { // Process using a and b }注意并不能用结构化绑定做所有事情试图让它们适应每种情况会使代码更加复杂。比如代码语言C自动换行AI代码解释if (auto [box, bit] std::pair(std::stoul(p), boxes.begin()); (bit boxes.find(box)) ! boxes.end()){ // Process if using both box and bit variables }此处box变量被定义为类型unsigned long并且具有从stoul(p)返回的初始值。对于那些不熟悉它的人来说stoul()是一个函数它以类型作为其第一个参数还有其他可选参数string- 包括std::string_base并将其内容解析为指定基数的整数默认为 10该函数作为无符号长整型值返回。