C++17 几个新的语言特性

1. 结构化绑定声明

绑定指定名称到初始化器的子对象或元素。 通俗地讲,对形如 std::pairstd::tuple 或自定义聚合类型的实例,可以声明一组变量直接指向其成员,无需创建一个对应聚合类型的临时变量:

1
2
3
4
std::pair<int, int> point{ 0, 1 };
// some operations around point
// ...
auto [x, y] = point; // x equals to point.first; y equals to point.second

引用语法同样适用:

1
2
3
4
// keys to values
std::pair<std::vector<int>, std::vector<int>> kv;
// ...
auto&& [keys, values] = kv;

不过不能绑定到已声明变量上,还是要通过 std::tie() 绑定:

1
2
3
4
int x = 0, y = 0;
std::pair<int, int> point{ 0, 1 };
[x, y] = point; // 不支持这种语法
std::tie(x, y) = point; // Ok

2. if & switch 内初始化语句

类似 for 循环的语法:if (init; condition)switch (init; condition)

可以将 init 语句内声明的变量的生命周期限制在 if elseswitch case 块内。优点显而易见,再也不用在外层作用域预先声明变量使其在 if else 内都可见了。极大程度上避免了名字污染问题。

1
2
3
4
if (const auto pos = myString.find("World"); pos != std::string::npos)
std::cout << pos << " World\n";
else
std::cout << pos << " not found!!\n";

3. 类模版的模版参数推断

模版参数类型推断终于普及到类模板了。在这之前,创建一个 std::pair 对象最常用的方法不是直接构造,而是通过 std::make_pair() 函数:

1
auto myPair = std::make_pair(42, "hello world");

为的就是利用函数模板的参数类型推断能力,可以少敲几下键盘。现在,类模版同样可以推断参数类型了:

1
2
3
4
5
// before: 
// std::pair<int, const char*> myPair{ 42, "hello world" }

// now:
std::pair myPair{ 42, "hello world" };

对其它所有类模版同样适用:

1
2
3
4
5
// before:
// std::array<int, 3> arr{ 1, 2, 3 };

// now:
std::array arr{ 1, 2, 3 };
1
2
3
4
5
// before:
// std::lock_guard<std::mutex> _(mtx);

// now:
// std::lock_guard _(mtx);

4. 折叠表达式

展开一个参数包并对其中参数依次施以动作。算是对 C++11 里引入的变参模版的应用规则的一种优化吧。

折叠表达式支持 4 种形式的语法表达:

  • ( pack op ... )
  • ( ... op pack )
  • ( pack op ... op init )
  • ( init op ... op pack )

这里有详细的解释,比我在这胡说八道强多了。

一个经典的应用场景是日志打印:

1
2
3
4
5
6
7
8
// 第四种语法的表现
template <class... Args>
void print(Args&&... args)
{
(cout << ... << std::forward<Args>(args)); // 注意:整个表达式以 () 包含。
}

print(1, 3, "hello"); // 打印出:13hello

输出的参数全挤在一起,这肯定不是想要的结果。可以改造下:

1
2
3
4
5
6
7
8
// 第一种语法的表现
template <class... Args>
void print(Args&&... args)
{
((cout << std::forward<Args>(args) << " "), ...);
}

print(1, 3, "hello"); // 打印出:1 3 hello

逗号左边两次输出以 () 包含,变成了一个表达式,等同于第一种语法的 pack 部分;‘,’ 即 op。整个表达式的意思就是对参数包内所有参数,先打印参数本身再打印一个空格符,依次操作。

参考资料:

《C++17 in detail》

structured binding declaration

fold expression

评论