微信号:cppFans

介绍:伯乐在线旗下账号,「CPP开发者」专注分享 C/C++ 开发相关的技术文章和工具资源.

C++17 相比于 C++14 的所有重大变化

2017-07-12 17:00 CPP开发者

(点击上方公众号,可快速关注)


英文:open-std,编译:d41d8c  

https://zhuanlan.zhihu.com/p/27551890

如有好的文章投稿,请点击 → 这里查看详情


摘要


本文档列举了自C++14发布以来,到C++17 DIS(N4660)发布为止,应用于C++工作草案(working draft)的所有重大变化。重大变化(major changes)是以专门的文件(paper)的形式加入的。不是每个文件都单独在此提及,没有单独提及的文件在下面简要列出。CWG或LWG问题清单(issue list)中的问题解决方案(issue resolution)通常不包括在重大变化之内,但是包含问题解决方案的文件也会简要列出。


目录


  • 删除或弃用的特性

  • 具有全局影响的新的核心语言特性

  • 具有局部影响的新的核心语言特性

  • 新的库特性

  • 对现有特性的修改

  • 杂项

  • 未列入的文件

  • 展示C++17的代码片段合集


删除或弃用的特性


文件:N4086

概要:删除trigraph

注释:字符序列 ??! 不再表示 | 。实现(implementation)可以把类似trigraph的功能作为输入编码的一环。


文件:P0001R1

概要:删除register

注释:register仍然是保留字,但是不再具有任何语义。


文件:P0002R1

概要:删除bool类型的++运算

注释:前自增和后自增运算符不再接受bool类型的操作数。


文件:P0003R5

概要:删除throw(A, B, C)

注释:形如throw(A, B, C)的动态异常规范(dynamic exception specification)不再是合法的。throw()保留,成为noexcept(true)的同义词。注意throw()的语义变化。


文件:P0386R2

概要:弃用static constexpr成员的重复声明

注释: 对于struct X { static constexpr int n = 10; };,int X::n;不再是定义,而是多余的重复声明,这种重复声明已被弃用。成员X::n是隐含的inline变量(见下文)。


———-


文件:N4190

概要:删除auto_ptr、random_shuffle、<functional>中的过时部分

注释:在C++11已经被弃用并被更好的组件取代的功能将不再包含于C++17中。它们的名字仍然是保留的,实现可以选择继续提供。


文件:P0004R1

概要:删除被弃用的iostream成员

注释:同上


文件:P0302R1

概要:删除std::function的内存分配器(allocator)支持

注释:多态函数包装器function不再具有接受内存分配器的构造函数。内存分配器支持对具有类型擦除(type-erase)功能的可复制类型来说很困难,可能无法有效实现。


文件:P0063R3(见下文)

概要:弃用一些C库(C library)头文件(header)

注释:“C库”(C library,这个术语指的是C++标准库的一部分, 而不是C标准库的一部分)中的以下头文件被弃用:<ccomplex>、<cstdalign>、<cstdbool>、<ctgmath>。注意<ciso646>头文件没有被弃用。


文件:P0174R2

概要:弃用标准库中的陈旧部分

注释:以下组件被弃用:allocator<void>、raw_storage_iterator、get_temporary_buffer、is_literal_type、std::iterator。


文件:P0618R0

概要:弃用<codecvt>

注释:整个<codecvt>头文件(注意codecvt类并不在这个头文件中)被弃用。wstring_convert和wbuffer_convert也被弃用。这些功能难以正确使用,甚至有怀疑它们的规范是否正确。用户应该使用专用的文本处理库。


文件:P0371R1

概要:暂时弃用memory_order_consume

注释: 当前的“consume”内存序的语义被发现是不够的,需要重新定义。这项工作希望能在C++的下一版中完成。在进行这项工作的时间里,建议用户不要使用“consume”内存序,而是使用“acquire”内存序,以免将来遇到问题。


文件:P0521R0

概要:弃用shared_ptr::unique

注释:该成员函数暗示了没有实际提供的行为。


文件:P0604R0

概要:弃用result_of

注释:请改用新的trait:invoke_result。


具有全局影响的新的核心语言特性


这些是在不需要你知情或同意的情况下,你可能遇到的特性。


文件:P0012R1

概要:异常规范(exception specification)作为类型系统的一部分

注释:函数的异常规范现在是函数类型的一部分:void f() noexcept(true); 和 void f() noexcept(false); 是具有不同类型的函数。函数指针可以按符合常理的方式转换。(但是这两个函数 f 不能构成重载。)这个变化加强了类型系统。例如,API可以通过类型系统要求回调函数不抛出异常。


文件:P0135R1

概要:保证的复制消除(guaranteed copy elision)

注释:prvalue和glvalue的含义已被修改,prvalue不再代表对象(object),而仅仅代表“初始化”(“initialization”)。返回prvalue的函数不再会复制对象(强制复制消除“mandatory copy elision”),并且有了新的从prvalue到glvalue的转换,叫做temporary materialization conversion。这个变化意味着复制消除是保证了的,而且甚至可以应用于不可移动或复制的类型。这允许你定义返回这种(不可移动或复制的)类型的函数。


文件:P0035R4

概要:过度对齐类型(over-aligned types——即alignment超过std::max_align_t的类型)的动态内存分配

注释:动态内存分配(operator new)现在可以支持过度对齐的类型,这个运算符的一个新的重载接受对齐要求(alignment)参数。仍然是由实现决定支持哪些对齐。


文件:P0145R3

概要:更严格的表达式求值顺序

注释:对某些子表达式的求值顺序的规定更多了。这个变化的一个重要方面是函数的各个实参现在以不确定(indeterminate)的顺序的求值(也就是说没有交错 interleaving),而以前只是未指定的(unspecified)。注意重载运算符的求值顺序和调用方式有关:如果按照运算符的形式调用,就和相应的内置运算符的顺序相同;如果按照函数调用的形式调用,就和一般的函数调用相同(也就是不确定的 indeterminate)。


具有局部影响的新的核心语言特性


这些是如果你要使用,你就需要知道的特性。


文件:N4267

概要:u8字符字面量

注释:具有u8前缀的字符字面量产生一个在UTF-8中占有一个编码单元(code unit)的合法Unicode代码点(code point)对应的字符,换句话说,就是产生一个ASCII值。例如:u8’x’。


文件:P0245R1

概要:十六进制浮点数字面量

注释:具有十六进制底数和十进制指数的浮点数字面量: 0xC.68p+2, 0x1.P-126。C语言自从C99开始就支持这种语法, printf的%a可以输出这种形式的浮点数。


文件:N4295, P0036R0

概要:fold表达式

注释:用于迭代地将二元运算符应用于参数包(parameter pack)的元素的便利语法:template <typename …Args> auto f(Args …args) { return (0 + … + args); }


文件:P0127R2

概要:template <auto>

注释:模板的非类型形参可以用占位符类型auto声明。例如:

• template <auto X> struct constant { static constexpr auto value = X; };

• Delegate<&MyClass::some_function>

(←_←第二个例子的完整版本见下文)


文件:P0091R3, P0512R0, P0433R2, P0620R0

概要:类模板参数推导

注释:类模板的模板参数现在可以从构造函数推导。例如 pair p(1, ‘x’); 定义 p 为 pair<int, char> 类型的变量(这不是HTML错误,模板实参是故意省略的)。显式的推导指引(deduction guide)是隐式推导的补充,显式的推导指引允许作者定制推导如何发生,或者禁止推导。


文件:P0292R2

概要:Constexpr if

注释:新的 if constexpr (condition) 语句根据常量表达式(constant expression)的值选择执行哪个分支。在模板的实例中,只有在条件(condition)具有合适的值的时候,对应的分支才会实例化。


文件:P0305R1

概要:带初始化的选择语句

注释:选择语句 if 和 switch 有了一个新的、可选的初始化部分:if (auto it = m.find(key); it != m.end()) return it->second;


文件:P0170R1

概要:Constexpr lambda

注释:lambda表达式现在可以是常量表达式了:auto add = [](int a, int b) constexpr { return a + b; }; int arr[add(1, 2)];


文件:P0018R3

概要:lambda捕获*this

注释:以前:[self = *this]{ self.f(); } 现在:[*this]{ f(); }


文件:P0386R2, P0607R0

概要:inline变量

注释:在头文件中:inline int n = 10; 所有定义都指代同一个实体。static constexpr 成员变量隐含地成为 inline 变量。(标准库中的常量,不论是已有的还是新增的,都已使用inline。)


文件:P0217R3, P0615R0

概要:结构化绑定

注释:auto [it, ins] = m.try_emplace(key, a1, a2, a3);

可以分解数组、所有成员都是public的类、像pair、tuple和array一样遵循get<N>协议的自定义类型。


文件:P0061R1

概要:__has_include

注释:检查能否包含特定头文件的预处理运算符。


文件:P0188R1 P0189R1 P0212R1

概要:属性(attribute)[[fallthrough]]、[[nodiscard]]、[[maybe_unused]](分别对应三个文件)

注释:一套新的标准化属性。属性没有必要的语义,但是鼓励实现发出或抑制适当的诊断(警告)。


文件:P0137R1

概要:launder

注释:语言支持工具(“优化屏障optimisation barrier”),允许库重新使用存储(storage),并通过旧指针访问该存储(以前是不允许的)。(这是实现者的专家工具,预期不会在“正常”代码中出现)。


文件:P0298R3

概要:字节类型

注释:新的类型byte在<cstddef>中定义(不在<stddef.h>,并且只定义在命名空间std中),它和unsigned char具有相同的布局,和现有的字符类型一样允许别名(aliasing),并且定义了按位操作。


新的库特性


文件:P0226R1

概要:数学特殊函数(mathematical special functions)

注释:前国际标准ISO/IEC 29124:2010(数学特殊函数)的内容现在是C++的一部分。 这些函数只添加到<cmath> ,而没有添加到<math.h> ,并且只定义在命名空间std中。


文件:P0218R0, P0219R1, P0317R1, P0392R0, P0430R2, P0492R2

概要:文件系统

注释:文件系统技术规范(Filesystems Technical Specification)的内容现在是C++的一部分。 文件系统库允许以可移植的方式与目录和类似目录的结构进行交互(列出文件目录的内容,移动文件等)。 它主要以POSIX为模型,但它足够灵活,可以在各种系统上实现。


文件:P0024R2, P0336R1, P0394R4, P0452R1, P0467R2, P0502R0, P0518R1, P0523R1, P0574R1, P0623R0

概要:并行

注释:并行技术规范(Parallelism Technical Specification)的内容现在是C++的一部分。它为很多算法(algorithm)增加了重载,这些新的重载额外接受一个执行策略(execution policy)参数。它也加入了新的算法(见下文)。 支持三种执行策略,分别提供顺序(sequential),并行( parallel)和向量化(vectorized)的执行。


文件:P0024R2

概要:新算法

注释:并行技术规范为标准库添加了几种新算法。加入它们的动机是它们可以有效地并行执行,但也提供了通常的(不接受execution policy参数的)简单形式:for_each_n、reduce、transform_reduce、exclusive_scan、inclusive_scan、transform_exclusive_scan、transform_inclusive_scan。请注意, reduce看起来与现有的accumulate相似,但reduce不保证任何特定的操作顺序。


文件:P0220R1, P0254R2, P0403R1

概要:新类型:string_view(以及basic_string_view)

注释:新的string_view类是API接口的推荐类型,如果API需要读取字符串的内容,但是不需要取得字符串的所有权或者修改字符串。它可以从char指针构造,但是其他的字符串类型应该自己提供到string_view的隐式转换。


文件:P0220R1, P0032R3, P0504R0

概要:新类型:any

注释:类型any对可复制(copyable)的对象进行类型擦除(type-erase)。基本上可以用any做以下三件事:1. 把T类型的值放入其中。2. 复制它。 3. 检查它是否包含一个U类型的值,并且取出这个值,取出操作只有在U就是T的时候才会成功。


文件:P0088R3, P0393R3, P0032R3, P0504R0, P0510R0

概要:新的类模板:variant

注释:variant是一个可辨识联合(disjoint union / discriminated union)。variant<A, B, C> 类型的值在任何时间都包含一个类型为A、B或C之一的值。


文件:P0220R1, P0307R2, P0032R3, P0504R0

概要:新的类模板:optional

注释:一个可选的值。一个optional<T>表示一个T值,或者表示没有值(由类型nullopt_t标记)。在某些方面,可以认为它等同于variant<nullopt_t, T>,但是它具有专门的接口。


文件:P0220R1

概要:新算法:sample

注释:从一个范围(range)里均匀选取最多n个元素作为样本。


文件:N4169

概要:invoke

注释:以统一形式调用Callable实体(包括函数、函数对象和成员指针)的工具。这允许用户编写的库可以使用与标准的魔法(magic)INVOKE规则相同的行为。


文件:P0077R2, P0604R0

概要:is_invocable, is_invocable_r, invoke_result

注释:用于推断可调用性和调用结果的trait。(←_←看名字就知道应该和std::invoke配合使用。C++11的result_of也应该和invoke配合使用来着,如果只支持函数和函数对象应该直接用decltype,可惜名字取得不好,造成不少误用)


文件:P0067R5

概要:基本字符串转换

注释:函数to_chars和from_chars分别生成和解析数字的字符串表示。这些旨在形成用于替换printf和iostream格式化操作的高效、低级(low-level)的基础。它们遵循惯用的C++算法风格。


文件:N3911

概要:类型别名模板void_t

注释:template <class…> using void_t = void; 在元编程中惊人地有用,简化了SFINAE的使用。


文件:N4389

概要:类型别名模板bool_constant

注释:template <bool B> using bool_constant = integral_constant<bool, B>


文件:P0013R1

概要:逻辑操作元函数

注释:用于元编程的变长元函数conjunction、disjunction和negation。这些trait在元编程的意义上是短路(short-circuit)的:不影响结果的模板不会被实例化。


文件:P0185R1

概要:用于SFINAE友好(SFINAE-friendly)swap的traits

注释:新的trait is_swappable、is_nothrow_swappable、is_swappable_with、is_nothrow_swappable_with。


文件:LWG 2911

概要:Trait is_aggregate

注释:检查类型是否是聚合类(aggregate)。例如,可以用于判断泛型类型(←_←就是作为模板参数的类型)是应该用列表初始化(list-initialization)还是应该用非列表初始化(non-list-initialization)。


文件:P0258R2

概要:Trait has_unique_object_representations

注释:这个trait可以用来判断特定的基于值的操作(例如比较和求hash值)是否可以用基于对象表示(object representation)的操作(例如memcmp)代替。


文件:P0007R1

概要:as_const

注释:给定lvalue x,std::as_const(x)返回相应的带const限定符的lvalue。std::as_const不能接受rvalue。


文件:N4280

概要:非成员函数size、data、empty

注释:新增的函数补充了现有的非成员函数begin、end等等。通过这些函数,可以用统一的方式访问标准容器和C风格数组。注意,和begin/end不同,这些新的函数不是定制点(customisation point),仅为方便起见提供。(←_←不知道原文作者为什么强调 the new functions are not customisation points,记得customization point是Ranges里的术语,而且在Ranges里这些非成员函数确实是customization point object)


文件:P0025R0

概要:clamp

注释:clamp(x, low, high)在x处于区间[low, high]内时返回x,否则返回最接近的边界(小于low就返回low,大于high就返回high)。


文件:P0295R0

概要:gcd和lcm

注释:数论中的函数,计算两个整数的最大公约数和最小公倍数。


文件:N4508

概要:shared_mutex类

注释:读-写互斥体,能够以共享或独占模式锁定。


文件:P0154R1

概要:干扰尺寸(interference sizes)

注释:两个新的实现定义常量hardware_constructive_interference_size、hardware_destructive_interference_size允许平台记录其高速缓存行大小(cache line size),以便用户避免虚假共享(破坏性干扰)并提高局部性(建设性干扰)。定义了两个单独的常量,以支持异构体系结构(heterogeneous architecture)。


文件:P0220R1

概要:元组(tuple)apply

注释:调用一个callable,参数从给定的元组中提取。


文件:P0209R2

概要:从元组构造

注释:新的函数模板make_from_tuple,功能是用给定元组的成员初始化T类型的值。它有点像刚提到的apply,但是它用于构造函数。


文件:P0005R4, P0358R1

概要:通用否定函数对象not_fn

注释:一个调用包装器(call wrapper),对它包装的callable取否定。它适用于具有任意数量参数的callable。它取代了旧的not1和not2包装器。


文件:P0220R1

概要:memory resource

注释:一套新的组件,包括用于动态选择内存提供者的memory resource基类,以及三个具体实现(synchronized_pool_resource、unsynchronized_pool_resource、monotonic_buffer_resource)。有关用例参见下一条。


文件:P0220R1, P0337R0

概要:多态的内存分配器

注释:使用memory resource的内存分配器,memory resource可以在运行时改变,不是分配器类型的一部分。为方便使用,还提供了类型别名,如std::pmr::vector<T> = std::vector<T, polymorphic_allocator<T>>。


文件:P0220R1, P0253R1

概要:搜索器函数对象

注释:使用Boyer-Moore和Boyer-Moore-Horspool算法、用于搜索子字符串的函数对象,和一个使用这些函数对象的algorithm。


对现有特性的修改


文件:N3928

概要:只接受一个参数的static_assert

注释:static_assert声明不再需要第二个参数:static_assert(N > 0);


文件:N4230

概要:嵌套命名空间声明

注释:namespace foo::bar { /* … */ }


文件:N4051

概要:允许用typename声明模板的模板参数(template template parameters)

注释:template <template <typename> typename Tmpl> struct X; 以前,模板的模板参数的声明只能用关键字class。


文件:P0184R0

概要:基于范围的for循环(range-based for)接受不同的begin/end类型

注释:for (decl : expr) 的规则受到重写,从以前的auto __begin = begin-expr, __end = end-expr;改成现在使用的 auto __begin = begin-expr; auto __end = end-expr;。这使得基于范围的for循环为新的Ranges(工作正在进行中)做好准备。


文件: P0195R2

概要:using-declaration 中的包扩展(pack expansion)

注释:template <typename …Args> struct X : Args… { using Args::f…; };


文件:P0138R2

概要:固定枚举(fixed enum)值的构造

注释:类型为固定枚举 E 的变量可以用 E e { 5 }; 的形式定义,不再需要更啰嗦的 E e { E(5) };。


文件:N3922

概要:从花括号列表中推导auto的新规则

注释:以前,auto a{1, 2, 3}, b{1}; 是允许的,两个变量的类型都是 initializer_list<int>。现在 auto a{1, 2, 3}; 是错误的(ill-formed), auto b{1}; 声明一个 int。注意,auto a = {1, 2, 3}, b = {1}; 保持不变,推导出 initializer_list<int> 。此改变预期作为缺陷解决方案(defect resolution)(在C++11和C++14模式下也会使用新规则)。


文件:N4259

概要:uncaught_exceptions()

注释:函数uncaught_exception被弃用,新函数uncaught_exceptions返回计数而不是布尔值。以前的功能实际上是不能用的,N4152解释了细节。


文件:N4266

概要:命名空间(namespace)和枚举成员(enumerators)的属性(attributes)

注释:命名空间和枚举成员现在可以用属性标记。例如,这允许标记命名空间和枚举成员为弃用的(deprecated)。


文件:P0028R4

概要:属性命名空间(attribute namespace)不需要重复指定

注释:当重复使用某一个属性命名空间时,这简化了命名空间限定。


———-


文件:N4279

概要:改进了std::map和std::unordered_map的插入

注释:m.try_emplace(key, arg1, arg2, arg3) 在m中已经含有key的时候没有任何效果,否则插入一个从参数构造的新元素。这个接口(interface)保证即使实参绑定到右值引用(rvalue reference),如果没有发生插入,实参将不会被移动(not moved from)。


文件:P0084R2

概要:emplace的返回类型

注释:顺序容器(sequence container)的过去返回void的emplace、emplace_front、emplace_back成员函数模板,现在返回刚刚插入的元素的引用。(关联容器不受影响,因为它们的插入函数一直都返回相关元素的迭代器。)


文件: P0083R3, P0508R0

概要:set和map的拼接(splice)

注释:一个叫做节点句柄(node handle)的新机制加入了标准容器库。这个新机制允许在不同的map/set对象之间移植元素,而不会触及容器中的对象。此外,这个机制使得对提取(extract)出的键值进行可变访问成为可能。(←_←可以不使用mutable就修改set的元素和map的key了,当然在修改之前必须先提取出来,修改之后再插入回去,以保证set和map的内部结构不受破坏)


文件:P0272R1

概要:非const的string::data

注释:现在有了一个非const的basic_string::data重载,返回一个可变的(mutable)指针。此外,C++17允许向空终止符(null terminator)写入,只要写入的值是零。这使得字符串类更便于和C风格接口一起使用。


文件:P0156R0, P0156R2

概要:变长(variadic)版的lock_guard,名叫scoped_lock

注释:增加了变长的类模板scoped_lock<Args…>。它可以同时给多个可锁的对象(lockable object)加锁(使用和std::lock相同的算法),并且在析构函数中释放锁。一开始建议直接修改lock_guard,使它成为变长模板,但是发现这会造成ABI不兼容,所以现在我们有了新的类模板scoped_lock,它严格优于旧的lock_guard,所以不要使用lock_guard了。


文件:P0006R0

概要:变量模板trait

注释:对于每一个只包含一个静态成员常量的trait foo ,有了一个对应的变量模板foo_v<Args…>。foo_v<Args…>等价于foo<Args…>::value。


文件:P0152R1

概要:atomic::is_always_lock_free

注释:一个新的静态成员常量is_always_lock_free记录了给定原子类型(atomic type)的操作是否总是无锁(lock-free)的。现有的非静态成员函数可能对同一原子类型的不同值给出不同的答案。


文件:P0220R1, P0414R2

概要:数组的shared_ptr

注释:类模板shared_ptr现在可以支持C风格数组,只需传入T[]或T[N]作为模板参数。接受原始指针(raw pointer)的构造函数将会设置(install)合适的数组内存释放器(array deleter)。


文件:P0163R0

概要:shared_ptr::weak_type

注释:shared_ptr<T>现在有了一个成员类型weak_type,它是weak_ptr<T>的别名。这使得泛型代码不需要解构shard_ptr的类型就可以知道对应的weak_ptr类型。


文件:P0030R1

概要:三维的斜边长(hypotenuse)

注释:三维斜边长hypot(x, y, z) (可以用来计算三维空间中两个点的距离)作为额外的重载加入 <cmath> (但没有加入<math.h>,并且只定义在命名空间std中)。


文件:P0040R3

概要:更多用于未初始化内存的算法

注释:增加在未初始化的内存中构造对象和销毁对象的算法。包括分别进行默认初始化(default-initialization)和值初始化(value-initialization)的版本。


文件:N4510

概要:内存分配器对不完整类型(incomplete type)的支持

注释:这项改变放宽了内存分配器对它的值类型(value type)的要求,使得值类型可以是不完整类型。例如,这允许递归的结构,如:struct X { std::vector<X> data; };


文件:P0092R1, P0505R0

概要:对<chrono>的改变

注释:为时间点(time point)增加了向上取整(floor)、向下取整(ceiling)、除法(division)和四舍六入五成双取整(rounding)。(←_←我们中出了一个除法。根据P0092R1来看,原文似有问题,增加的应该是绝对值abs而不是除法。)大多数成员函数成为constexpr函数。


文件:P0426R1

概要:char_traits的constexpr支持

注释:对于标准要求的所有的char_traits特化(也就是std::char_traits<char>、std::char_traits<wchar_t>、std::char_traits<char16_t>、std::char_traits<char32_t>这四个类),成员函数length、compare、find和assign现在都是constexpr成员函数了。这使得string view可以在常量表达式中更广泛地使用。


文件:N4387

概要:改善pair和tuple

注释:这个变化使得pair和tuple的构造函数变为“带条件的explicit”构造函数。只有在有某个元素的对应构造函数是explicit的时候,pair和tuple的构造函数才是explicit的。


文件:P0435R1, P0548R1

概要:对common_type的改变

注释:因为如果不改一下common_type,就不能称为新标准了……


杂项


文件:P0063R3

概要:C++引用C11

注释:C++标准现在正式地(normatively)引用C11 (ISO/IEC 9899:2011) 为“C标准”(“The C Standard”)了。这不仅是应对ISO的要求(ISO要求对其他国际标准的引用必须引用最新发布的版本,而不是某个历史版本),也使得我们可以使用aligned_alloc,它对于改进动态内存管理(dynamic memory management)非常有用。


文件:P0180R2

概要:保留的(reserved)命名空间

注释:所有形如 stdN (其中 N 是一串数字)的命名空间现在被保留了。(用户不能使用了。)


文件:P0175R1

概要:C库总览(synopsis)

注释:一个纯粹非技术性(editorial)的变化:标准库的“C库”部分的所有头文件的内容现在在C++标准文档中以完整的总览(synopsis)的形式展现(也就是完整列出每个标准头文件包含的完整声明),而不再是像以前一样只列出名字。这使得与C语言中的语义不同的地方(例如增加的重载,针对language linkage的重载)更容易理解。


文件:N4262 P0134R0 P0391R0 N4284

概要:术语“forwarding reference”、“default member initializer”、“templated entity”、“contiguous iterator”(分别对应四个文件)

注释:这些变化没有正式的(normative)影响,但是它们为迄今为止只从语言规则中出现的概念建立了官方术语。精确和众所周知的术语简化了关于C++的讨论,也简化了规范。


未列入的文件


以下文件在委员会会议(committee meeting)上被接受,但是它们的内容过于专门,不能称为单独的特性:N4258, N4268, N4277, N4285, P0017R1, P0031R0, P0074R0, P0270R3, P0283R2, P0296R2, P0503R0, P0509R1, P0516R0, P0517R0, P0558R1, P0599R1, P0612R0。


以下文件包含被接受为缺陷报告(defect report)的问题(issue)。处理CWG问题的文件有 N4192, N4457, P0164R0, P0167R2, P0263R1, P0384R0, P0398R0, P0490R0, P0507R0, P0519R0, P0520R0, P0522R0, P0575R1, P0576R1, P0613R0, P0622R0。处理LWG问题的文件有 N4245, N4366, N4383, N4525, P0165R0, P0165R1, P0165R2, P0165R2, P0165R3, P0165R4, P0304R1, P0397R0, P0610R0, P0625R0。每份文件中只有部分问题被选出,具体细节见会议记录(meeting minutes)。以下单独的文件解决特定的问题:N4089, N4261, P0033R1, P0136R1, P0250R3, P0418R2, P0513R0。历年来解决的问题(accepted issue)的完整列表公布在CWG issues list和LWG issues list,二者会经常更新。


展示C++17的代码片段合集


std::unordered_map<std::string, std::unique_ptr<Foo>> items;

std::vector<std::unique_ptr<Foo>> standby;

 

// 如果没有'id'元素,则把'foo'作为'id'元素插入。

// 否则把'foo'保存起来以备之后使用,将其放入standby中。

 

// C++17以前

 

void f(std::string id, std::unique_ptr<Foo> foo) {

  auto it = items.find(id);

  if (it == items.end()) {

    auto p = items.emplace(std::move(id), std::move(foo));

    p.first->second->launch();

  } else {

    standby.push_back(std::move(foo));

    standby.back()->wait_for_notification();

  }

 

  // 注意:

  // * 变量'id'不能再次使用(moved-from);或者……

  // * ……需要使用'const string& id'作为参数,并强制复制。

  // * 做了两次map查找。有序的map可以使用lower_bound + hint,但无序的map(unordered map)用不了。

  // * (不能无条件调用emplace,因为它可能导致*foo被析构。)

}

 

// 使用C++17

 

void f(std::string_view id, std::unique_ptr<Foo> foo) {

  if (auto [pos, inserted] = items.try_emplace(id, std::move(foo)); inserted) {

    pos->second->launch();

  } else {

    standby.emplace_back(std::move(foo))->wait_for_notification();

  }

}


下面的代码片段展示了template <auto>的用法,例子中的类模板将一个非成员函数转发给绑定了类实例的成员函数,并且成员函数是这个类模板的类型的一部分。


// C++17以前

 

template <typename T, int (T::* MF)(int, int)> // 两个参数:一个类型参数,一个非类型参数

struct Delegate { /* ... */ };

 

int n = Delegate<MyComplexClass, &MyComplexClass::an_imporant_function>(&obj)(10, 20);

 

// 使用C++17

 

template <auto> struct Delegate;                  // 一个(非类型)参数

template <typename T, int (T::* MF)(int, int)>

struct Delegate<MF>  { /* ... */ };              // 实现方法和以前相同,但现在使用偏特化(partial specialization)

 

int n = Delegate<&MyComplexClass::an_imporant_function>(&obj)(10, 20);


下面的代码片段展示fold表达式在泛型代码中的用法。


// 对于参数包f中的所有元素,调用f(n)。

template <typename ...F>

void ApplyAll(int n, const F&... f) {

   (f(n), ...); // 一元fold (over the comma operator)

}

 

// 对于参数包f中的所有元素,计算f(a, b),返回和。

template <typename ...F>

int ApplyAndSum(int a, int b, const F&... f) {

   return (f(a, b) + ... + 0); // 二元fold

}


下面的代码片段展示shared_ptr对数组的支持。


// C++17以前

std::shared_ptr<char> p(new char[N], std::default_delete<char[]>()); // 如果不提供删除器(deleter)就错了

 

// 使用C++17

std::shared_ptr<char[]> p(new char[N]); // 删除器自动使用“delete[]”


看完本文有帮助?请分享给更多人

关注「CPP开发者」,提升C/C++技能

 
CPP开发者 更多文章 默默维护 30 年,glibc 创始人兼维护者辞职 用 C 语言写一个简单的 Unix Shell(1) 软件开发的难点在于沟通,10年编程经历的总结 不要自称是程序员,我十多年的 IT 职场总结 C++ 中命名空间的 5 个常见用法
猜您喜欢 ​11个日薪500的PHP程序员面试提问(附答案) 情怀和信念 Kafka设计解析(五)- Kafka Consumer设计解析 乐观锁的一种实现方式——CAS 「Swift」Vapor系列教程(1)