C++17 将有什么变革?

jopen 10年前

这是我对当前在厄巴纳举行的C++委员会会议上的提案方面文章的第三部分。 这次是全部关于子组变革的,对这方面,我写了很多文章,所以这仅仅是第一部分。 前面那些部分是关于并发(concurrency)的, 然后 第二部分是关于核心,网络,模型以及未定义行为方面的

变革

N4126 - 显式默认比较操作符

这个提议已经在上一部分的反射部分提到了,在那里我们用反射和std::tie解决这个问题。这份提议想让 = default变得合法; 对于所有的操作符 (>,<,==,!=, ...)。这些操作符仍然使用友元方法来实现,如下例所示:

class Thing  {      int a, b;  public:      // ...      friend bool operator<(const Thing&, const Thing&) = default;      friend bool operator>(const Thing&, const Thing&) = default;      friend bool operator<=(const Thing&, const Thing&) = default;      friend bool operator>=(const Thing&, const Thing&) = default;  };

N4127 - 检查后的解引用条件

因为已经提议过有范围的for(ranged-for)循环,这个提议转移到auto和默认类型.如果没有提供类型,它也希望,在其它语句中,引进或采用有范围的for的语法,使之合法,例如if语句:

std::weak_ptr<foo> wp;  if(x: wp.lock())

对于C++来说,这是一个巨大的改变,并且不是每个人都喜欢它,特别地,编译器的创建者不得不处理这所波及的范围.然而,如果委员会认可了下个版本的有范围的for循环,那么在其他C++控制结构,如if,while,for等,中允许同样的语法也就说得通了.

N4131 - 显式的绝不应该是隐式的

这篇文章对应的是N4074,它争论提议的变化内容,即让{expr}显式的返回是错误的,因为显式的决不应该是隐式的.关于草案中的更多细节,作者甚至给出了一个例子,说N4074提案可能导致未定义的行为.

N4135 -运行时按规定校验的语言支持 (修订8)

作者提供了很好的概述:

如果足够仔细,我们可以创建本质上无缺陷的库.但是即便是最好的库,只要使用不当,都可能产生灾难性的的后果.运行时按规定校验的实际应用:当函数调用时,检测前缀条件的过程,有助于在早期测试中,发现误用的情况.同时也有助于提升开发速度,增强软件健壮性.将按规定校验拓展为开发阶段而不仅仅是早期测试阶段,将带来更多长远的益处.

N4147 - 内联变量,或封装表达式

作者再一次提供了很好的概述:

内联函数为对外无法暴露对象的接口,提供了合适的行为.尽管是不自然的样版,通常还是会鼓励用户使用它们来包装全局变量.其它的变通方法包括类的静态数据成员,枚举,宏和变量模板,所有这些都有奇怪的语法或缺陷,限制了适用性.这个提议在变量定义上面指定了inline标识符,这意味着在语义上类似于内联函数的评估和链接.更通用的,这为命名值或非持续性变量提供了一种工具,它可以替代或补充 各种各样的变通方法.

N4148 - 不允许来自易拷贝类型的不可访问操作符

当前对于易拷贝类型(trivially copyable type)的定义有一个缺陷:它对于不可访问的/删除的 拷贝/移动 构造函数和赋值操作符同样有效.一个易拷贝对象也可以通过使用std::memcpy来拷贝,特别是当有一个易拷贝对象数组的时候,这在性能上更优.然而,对于一个删除的/不可访问的 拷贝/移动 构造函数或者赋值操作符,这样做是不明智的.作者提议更新标准里面的措辞,并提高特性,让std::is_trivially_copyable针对不可访问的/删除的 拷贝/移动 构造函数和操作符返回错误.

N4149 - 明确且合适的类

同样的,这篇文章也有很好的概述:

一些类只在某些上下文中工作: 作用域守护(scope guard)作为子表达式通常是没有用处的,表达式模板占位符作为局部变量也会出现问题.一个没有使用的函数的结果可能意味着调用者会使用不同的协议,如std::async.这个提议拓展了类的声明来阻止这类错误,并增加了通过类型替换自动解决它们的技术,例如一个表达式模板的值类型.另外,不可移动的对象的生成变得更加容易处理.

增加的功能包括"自动评估"提议里面提到的.这个提议更具表现力,可以更广泛的应用,并且易于接受和使用.

N4150 - 别名集合属性: 针对C++中restrict一样的别名语义

这个提议想增加restrict修饰符到C++,因为它已经存在于C99之中,同时,一些编译器已经提供了restrict作为一个C++拓展。由于目前没有定义restrict修饰符,作者寻求定义如何正确的使用它,特别是在C++ 11中像lambda一样的特性。

N4152 - 未捕获的异常

这篇文章想提高std::uncaught_exception的可用性:

函数 int std::uncaught_exceptions() 返回了异常对象的数目,这些对象已经初始化,并被抛出或者被重新抛出,但是没有激活任何处理程序.

N4154 - 操作符断言

这个提议想让assert(断言)成为语言结构,而不是一个宏定义:

assert宏定义从未表现得像一个真正的函数,在可以预见的未来,它将更像是一个操作符.在C中宏定义的表现方式,阻止了生产模式下的优化,但是在调试模式下却允许产生任意的副作用.增加assert作为一个关键字和内置的操作符将产生益处,而不会存在任何副作用.

N4160 - 值约束

这篇文章分析了如何在C++中支持契约式编程(contract programming)风格的特性.它尝试着提供一个概况,关于如何在编译时而不是运行时检查的时候支持契约.作者定义了文档的范围,如下:

鉴于[N1962] 是一篇关于在C++之中增加契约式编程支持的相当完整的提议,这个文档提供的是问题范围,而不是一个特定的提议.我们集中于确认期望值,潜在的实现困难以及代价.

我们知道的其它契约式编程提议 — [N4075], [N4110] — 建立在一个假设之上.即前置条件的支持,必须以提供某种形式之下:在函数调用之前评估前置条件,偶尔取消前置条件的评估以及安装未履行契约的处理程序.在这篇文章中,我们不会想当然的做这种假设.运行时支持只是我们分析范围的一个子集.我们更细致的探索了一个可选方案:集中于静态分析.

N4164 - 前向引用

作为正式的通用引用, 类型T&& 总是右值(r-value)引用,除了作为模板参数或者使用了auto关键字。技术上仍然是右值引用,但是在这种情况下,表现上是非常不同。到目前为止,标准不识别这点,作者想介绍这个术语前向引用(forwarding reference),关于在模板和auto关键字之中的右值引用。

N4165 - 统一调用语法

成员函数的调用语法是x.f() 或者 x->f(),而非成员函数则是f(x).这在泛型编程代码中会是一个问题,因为没有统一的调用语法,而泛型编程代码必须决定是调用成员函数还是非成员函数。

为了解决这个问题,作者提议允许使用语法x./->f()来调用自由函数,如果函数的第一个参数是x的指针或者引用。这也很好的满足C的用法,当函数第一个指针参数是一个结构体的时候。作者使用FILE*和fseek作为例子。

N4166 - 可移动的初始化列表

当前,std::initializer_list不是可移动的,它是在移动语义变得重要之前设计的.同时,那个时候只提供拷贝语义看起来是足够的,但是今天的情况已经发生了变化.作者提议一个模板化版本的std::intializer_list,它继承于非右值intializer_list类型:

template< typename T >  struct initializer_list< T && >    : initializer_list< T > {

这个构造实现了所有权和移动语义.

N4172 - 命名参数

命名参数的语法看起来和提议中的 "有范围的for(ranged-for)循环" 类似。但是在近似(几乎一样)的语法表达下面,两者有不同的意义。在一个函数调用过程中,你可以像下面例子中这样对命名参数赋值:

void foo(int x, int y, int z);  foo(40,z: 30, y:20);

在这个例子里面的函数调用过程中,参数z的值是30, 参数x的值是40。另外,非命名参数不能放在命名参数之后。

N4173 - 点操作符

同样的,这篇文章也有很好的概述:

这个提议允许用户定义点操作符(操作符.()),这样我们可以提供"智能引用"类似于"智能指针".这个提议的要点在于,如果类Ref定义了操作符.(),那么默认在Ref对象上的每个操作都在操作符.()的结果之前.不管怎样,显式声明为Ref成员函数的操作,应用到Ref对象上时不需要前置.程序员可以通过声明操作符.()为private,来阻止指向一个引用对象的指针"渗透"到更大的程序之中.

N4174 - 调用语法: x.f(y) vs. f(x,y)

这个很像N4165 - 统一调用语法.简单的描述:

基本的建议是定义x.f(y)和f(x,y)是等价的.另外,为了增加兼容性和模块性,我建议探索,当查询可调用成员函数(或函数对象)时,忽略不可调用和不可访问的成员函数的可能性,.

N4175 - 系统设定的比较

这份文档广泛的讨论了在C++中预设的比较该是什么样子,以及它能够将比较运算符设置为默认值有什么意义。本文和N4126有很大关联,同时也N4239相对应。

N4176 - 对比较的思考

Bjarne总结了下关于预设比较的讨论:

这是一份摘录了系统预设比较草稿的部分讨论的摘要。它提出了许多提议并且根据一系列标准做了比较。我将会对关于比较标准的选择有关的评论尤其感兴趣(故意的双关语)。