C++14

维基百科,自由的百科全书
跳转至: 导航搜索

C++14C++的下一版标准的非正式名称。C++14计划作为C++11的一个小扩展,主要内容是漏洞修复和小的改进。C++14标准的委员会草案(Committee Draft)N3690于2013年5月15日发表。[1]工作草案(Working Draft)N3936已于2014年03月02日完成。

尽管“C++14”这一名称暗示它将会在2014年发布,它的发布时间并不是确定的。在很长一段时间内,C++11标准被称为C++0x,直到它最终延迟到2011年方才发布。

下面列出的特性是N3936中所描述的。最终标准化之前,部分内容可能有所修改。

新的语言特性[编辑]

以下为将在C++14中被加入语言核心的特性。

泛型的lambda[编辑]

在C++11中,lambda函数的形式参数需要被声明为具体的类型。C++14放宽了这一要求,允许lambda函数的形式参数声明中使用类型说明符auto[2]

auto lambda = [](auto x, auto y) {return x + y;}

泛型lambda函数遵循模板参数推导的规则。以上代码的作用与下面的代码相同:[3]

struct unnamed_lambda
{
  template<typename T, typename U>
    auto operator()(T x, U y) const {return x + y;}
};
auto lambda = unnamed_lambda();

Lambda捕获部分中使用表达式[编辑]

C++11的lambda函数捕获(capture)在外层作用域声明的变量的方法只有通过值拷贝或引用。这意味着lambda的值成员不可以是只能move的类型。[4] C++14允许被捕获的成员用任意的表达式初始化。这既允许了通过move捕获,也允许了任意声明lambda的成员,而不需要外层作用域有一个具有相应名字的变量。[2]

这是通过使用一个初始化表达式完成的:

auto lambda = [value{1}] {return value;}

lambda函数lambda 的返回值是1,说明value被初始化为1。被声明的捕获变量的类型会根据初始化表达式推断,推断方式与用auto声明变量相同。

使用标准函数std::move可以使之被用以通过move捕获:

auto ptr = std::make_unique<int>(10); //See below for std::make_unique
auto lambda = [ptr{std::move(ptr)}] {return *ptr;}

声明ptr{std::move(ptr)}使用了两次ptr。第一次使用声明了一个新的变量,但由C++的作用域规则,在捕获部分,这个变量还不在作用域内。所以第二个ptr表示之前在lambda之外声明的变量。[5]

函数返回类型推导[编辑]

C++11允许lambda函数根据return语句的表达式类型推断返回类型。C++14为一般的函数也提供了这个能力。C++14还拓展了原有的规则,使得函数体并不是{return expression;}形式的函数也可以使用返回类型推导。[6]

为了启用返回类型推导,函数声明必须将auto作为返回类型,但没有C++11的后置返回类型说明符:

auto DeduceReturnType();   //返回类型由编译器推断

如果函数实现中含有多个return语句,这些表达式必须可以推断为相同的类型。[7]

使用返回类型推导的函数可以前向声明,但在定义之前不可以使用。它们的定义在使用它们的翻译单元(translation unit)之中必须是可用的。

这样的函数中可以存在递归,但递归调用必须在函数定义中的至少一个return语句之后:[7]

auto Correct(int i) {
  if (i == 1)
    return i;               // 返回类型被推断为 int
  else
    return Correct(i-1)+i;  // 正确,可以调用
}
 
auto Wrong(int i)
{
  if(i != 1)
    return Wrong(i-1)+i;  // 不能调用,之前没有return语句
  else
    return i;             // 返回类型被推断为 int
}

另一种类型推断[编辑]

C++11中有两种推断类型的方式。auto根据给出的表达式产生具有合适类型的变量。decltype可以计算给出的表达式的类型。但是,decltypeauto推断类型的方式是不同的。特别地,auto总是推断出非引用类型,就好像使用了std::remove_reference一样,而auto&&总是推断出引用类型。然而decltype可以根据表达式的结果类别和表达式的性质推断出引用或非引用类型:[6]

int i;
int&& f();
auto x3a = i;              // x3a 的类型是 int
decltype(i) x3d = i;       // x3d 的类型是 int
auto x4a = (i);            // x4a 的类型是 int
decltype((i)) x4d = (i);   // x4d 的类型是 int&
auto x5a = f();            // x5a 的类型是 int
decltype(f()) x5d = f();   // x5d 的类型是 int&&

C++14将增加decltype(auto)的语法。这将允许auto声明使用decltype对于给定表达式的推断规则。

decltype(auto)的语法也可以用于返回类型推导,只需使用decltype(auto)代替auto来诱发。[7]

放松的constexpr限制[编辑]

C++11引入了constexpr函数的概念,这样的函数可以在编译期执行。它们的返回值可以通过计算所需的常量表达式(如模板的非类型参数)得出。然而C++11要求constexpr函数只含有一个将被返回的表达式(也可以还含有static_assert声明等其它语句,但允许的语句类型很少)。

C++14将放松这些限制。constexpr函数将可以含有以下内容:[6]

  • 任何声明,除了:
    • staticthread_local变量。
    • 没有初始化的变量声明。
  • 条件分支语句ifswitchgoto是不允许的。
  • 所有的循环语句,包括基于范围的for循环。
  • 表达式可以改变一个对象的值,只需该对象的生命期在常量表达式函数内开始。包括对有constexpr声明的任何非const非静态成员函数的调用。

对于调用非constexpr函数的限制仍然存在。所以如果使用基于范围的for循环,beginend的重载形式必须自身是constexpr。值得注意的是,std::initializer_list在本地和全局都具有constexpr声明的begin/end函数。

此外,C++11指出,所有被声明为constexpr的非静态成员函数也隐含声明为const(即函数不能修改*this的值)。这点已经被删除,非静态成员函数可以为非const[8]然而,只有当对象的生命期在常量表达式求值中开始,非constconstexpr成员函数才可以修改类成员。

变量模板[编辑]

C++之前的版本中,模板可以是函数模板或类模板(C++11引入了类型别名模板)。C++14现在也可以创建变量模板。包括特化在内,通常的模板的规则都适用于变量模板的声明和定义。[2][9]

聚合体成员初始化[编辑]

C++11新增member initializer,这是一个表达式,被应用到类作用域的成员上,如果构造函数没有初始化这个成员。聚合体的定义被改为明确排除任何含有member initializer的类,因此,他们不允许使用聚合初始化。

C++14将放松这一限制,[6]这种类型也允许聚合初始化。如果花括号初始化列表不提供该参数的值,member initializer会初始化它。[10]

二进制字面值[编辑]

C++14的数字可以用二进制形式指定。[6]其格式使用前缀0b0B。这样的语法也被JavaPythonPerlD语言使用。

单引号作为数字分位符号[编辑]

在没有数字分位符号的情况下,较长的数字很难阅读,例如很难看出237498123和237499123的区别。C++14引入单引号(')作为数字分位符号,使得前述数字分别可以写作237'498'123和237'499'123。[11]

AdaD语言JavaPerlRuby等程序设计语言使用下划线(_)作为数字分位符号,C++之所以不和这些程序设计语言保持一致,是因为下划线已被用在用户自定义的字面值的语法中,使用下划线作为数字分位符号会产生歧义。

新的标准库特性[编辑]

共享的互斥体和锁[编辑]

C++14增加了一类共享的互斥体和相应的共享锁[12][13]。起初选择的名字是std::shared_mutex,但由于后来增加了与std::timed_mutex相似的特性,std::shared_timed_mutex成为了更适合的名字。[14]

关联容器中的异构查找[编辑]

C++标准库定义了四个关联容器类。set和multiset允许使用者根据一个值在容器中查找对应的的同类型的值。map和multimap容器允许使用者指定键(key)和值(value)的类型,根据键进行查找并返回对应的值。然而,查找只能接受指定类型的参数,在map和multimap中是键的类型,而在set和multiset容器中就是值本身的类型。

C++14允许通过其他类型进行查找,只需要这个类型和实际的键类型之间可以进行比较操作。[15]这允许std::map<std::string,int> (其中int亦可以是其他任何类型)使用const char*,或任何可以通过operator<std::string比较的类型作为查找的参数。

为保证向后兼容性,这种异构查找只在提供给关联容器的比较运算符允许的情况下有效。标准库的std::less(set和map的默认比较算符)与std::greater允许异构查找。[16]

标准自定义字面值[编辑]

C++11定义了自定义字面值的语法,但标准库没有使用其中的任何一个。C++14增加了以下标准字面值:[15]

  • "s",用于创建各种std::basic_string类型。
  • "h"、"min"、"s"、"ms"、"us"、"ns",用于创建相应的std::chrono::duration时间间隔。

两个"s"互不干扰,因为表示字符串的只能对字符串字面值操作,而表示秒的只针对数字。[17]

通过类型寻址多元组[编辑]

C++11引入的std::tuple类型允许不同类型的值的聚合体用编译期整型常数索引。C++14还允许使用类型代替常数索引,从多元组中获取对象。[15]若多元组含有多于一个这个类型的对象,将会产生一个编译错误:[18]

tuple<string, string, int> t("foo", "bar", 7);
int i = get<int>(t);        // i == 7
int j = get<2>(t);          // Same as before: j == 7
string s = get<string>(t);  //Compiler error due to ambiguity

较小的标准库特性[编辑]

std::make_unique可以像 std::make_shared一样使用,用于产生std::unique_ptr对象。[2]

std::integral_constant将会具有一个返回常量值的operator()重载。[15]

全局std::begin/std::end函数之外,将会增加std::cbegin/std::cend函数,它们返回一个范围的开始/结束常量迭代器。

另见[编辑]

参考资料[编辑]

  1. ^ Committee Draft, Standard for Programming Language C++. 
  2. ^ 2.0 2.1 2.2 2.3 Sutter, Herb. Trip Report: ISO C++ Spring 2013 Meeting. [2013-6-14]. 
  3. ^ N3649 Generic (Polymorphic) Lambda Expressions (Revision 3). 
  4. ^ Move capture in Lambda. 
  5. ^ N3648 Wording Changes for Generalized Lambda-capture. 
  6. ^ 6.0 6.1 6.2 6.3 6.4 Wong, Michael. The View form the C++ Standard meeting April 2013 Part 1. C/C++ Cafe. [14 June 2013]. 
  7. ^ 7.0 7.1 7.2 N3638 Return type deduction for normal functions. [14 June 2013]. 
  8. ^ N3652 Relaxing constraints on constexpr functions. 
  9. ^ N3651 Variable Templates (Revision 1). 
  10. ^ N3653 Member initializers and aggregates. 
  11. ^ Single-Quotation-Mark as a Digit Separator. 
  12. ^ Wong, Michael. The View form the C++ Standard meeting April 2013 Part 3. C/C++ Cafe. [14 June 2013]. 
  13. ^ N3659 Shared locking in C++ Revision 2. 
  14. ^ N3891: A proposal to rename shared_mutex to shared_timed_mutex. [2014-4-11]. 
  15. ^ 15.0 15.1 15.2 15.3 Wong, Michael. The View form the C++ Standard meeting April 2013 Part 2. C/C++ Cafe. [14 June 2013]. 
  16. ^ N3657 Adding heterogeneous comparison lookup to associative containers (rev 4). 
  17. ^ N3642 User-defined Literals for Standard Library Types (part 1 - version 4). 
  18. ^ N3670 Wording for Addressing Tuples by Type: Revision 2.