C++17 详解 10
本文为 《C++17 in detail》 一书的中文渣中渣译文,不足之处还望指正。
3.4 constexpr
lambda 表达式
lambda 表达式自 C++11 引入后,就成为了现代 C++ 必不可少的一部分。C++11 的另一个重要特性是“常量表达式”——主要通过 constexpr
声明。C++17 允许这两个元素共存——你可以在一个常量表达式上下文中调用 lambda 。
C++11/14 里,下边的代码不能通过编译:
1 | auto SimpleLambda = [] (int n) { return n; }; |
以 -std=c++11
flag 在 GCC 下编译会报如下错误:
1 | error: call to non-'constexpr' function 'main()::<lambda(int)>' |
但是,换成 -std=c++17
代码就可以编译通过了!这是因为 C++17 里遵循标准 constexpr
函数规则的 lambda 表达式被隐式地声明为了 constexpr
。
这句话什么意思呢?有什么限制呢?
摘自 Standard - 10.1.5 constexpr
指示符 [dcl.constexpr]
译注:完整版的 C++17 标准草案发布在这里——有 1622 页。
一个被定义为
constexpr
的函数应满足如下条件:
- 不能是虚函数(13.3);
- 返回类型应是一个字面类型(译注:原文为 literal type,不是字面值的意思,标准里有明确的、复杂的定义。);
- 每一个参数的类型均应是字面类型;
- 函数体应是
= delete
或= default
,或是一个不包含如下内容的复合语句:
- 汇编实现,
- goto 语句,
- 标识符标签(译注:即同
goto
配合使用的val:
),try
块,- 非字面类型的,或静态的,或线程存储周期(译注:即
thread_local
)的,或未进行初始化的变量的定义。
译注:如我之前吐槽,C++ 的一大精通难点体现在,一个小小的特性关键字背后,是嵌套了 N 层的、晦涩的、没有任何表征的概念定义。
constexpr
可见一斑。
实践角度讲,如果想要你的函数或 lambda 在编译期被执行,函数体内不应调用任何非 constexpr
代码。比如,不能动态分配内存。
lambda 也可以显式地声明为 constexpr
:
1 | auto SimpleLambda = [] (int n) constexpr { return n; }; |
如果你违反了 constexpr
函数规则,会触发编译错误。
1 | auto FaultyLeakyLambda = [] (int n) constexpr { |
new
操作符是非 constexpr
的所以上边代码不会编译通过。
constexpr
lambda 在结合了即将到来的 C++20 的 constexpr
标准算法后将会是一个伟大特性。
扩展:本修改提案:P0170。
3.5 嵌套命名空间
命名空间允许把类型和函数分组到不同的逻辑单元。
比如,通常会把一个叫 XY 库里的类型和函数存储到一个叫 xy 的命名空间。如下所示,有一个 SuperCompressionLib
命名空间,它暴露了两个叫 Compress
和 Decompress
的函数:
1 | namespace SuperCompressionLib { |
如果你有两层或更多嵌套的命名空间,事情就变得有趣了。
1 | namespace MySuperCompany { |
C++17 里,嵌套命名空间可以以一种更紧凑的方式书写:
1 | namespace MySuperCompany::SecretProject::SafetySystem { |
这种语法令人愉悦,对有 C# 或 Java 经验的开发者也更容易使用。
C++17 里,标准库的许多地方也以这种新的嵌套命名空间特性被“压缩了”:
比如 regex
库。
C++17 里它的定义如下:
1 | namespace std::regex_constants { |
C++17 之前同样的代码被声明为:
1 | namespace std { |
上边的嵌套声明出现在 C++ 规范,但每一个 STL 实现可能都会不同。
扩展:本修改提案:P4230。
3.6 编译器支持
略。