C++14标准是 ISO/IEC 14882:2014 Information technology -- Programming languages -- C++ 的简称[1] 。在标准正式通过之前,原名C++1y。
C++14标准的委员会草案N3690于2013年5月15日发表。[2]
2014年8月18日,经过C++标准委员投票,C++14标准获得一致通过。草案文档经过一定的文字润色和修正之后,将在年内提交给ISO作为正式的C++标准发布。
下面描述的特性是委员会草案N3690中所描述的。
- 中文名
- C++14
- 外文名
- C++14
- 标准编号
- ISO/IEC 14882:2014
目录
- 1 新的语言特性
- ? 泛型的Lambda函数
- ? Lambda捕获表达式
- ? 函数返回类型推导
- ? 另一种类型推断
- ? 放松的constexpr限制
- ? 变量模板
- ? 聚合体成员初始化
- ? 运行期确定一维长度的数组
- ? 二进制字面值
- 2 新的标准库特性
- ? 共享的互斥体和锁
- ? Optional值
- ? 关联容器中的异构查找
- ? 标准自定义字面值
- ? 运行期确定长度的定长数组容器
新的语言特性
以下为是C++14中被加入语言核心的特性。
泛型的Lambda函数
在C++11中,lambda函数参数需要被声明为具体的类型。C++14放宽了这一要求,允许lambda函数参数类型使用类型说明符auto。
auto lambda = [] (auto x, auto y) {return (x + y); }
尽管使用了C++11的关键字auto,泛型Lambda函数并不遵循auto类型推导的句法,而是遵循模板参数推导的规则(它们相似,但并不是在所有情况下都是相同的)。以上代码与下面的等价:
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函数通过值拷贝或引用捕获在外层作用域声明的变量。这意味着lambda的值成员不可以是只能move的类型。 C++14允许被捕获的成员用任意的表达式初始化。这既允许了通过move捕获,也允许了任意声明lambda的成员,而不需要外层作用域有一个具有相应名字的变量。
这是通过使用一个初始化表达式完成的:
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之外声明的变量。
函数返回类型推导
C++11允许lambda函数根据return语句的表达式类型推断返回类型。C++14为所有的函数提供了这个能力。C++14还拓展了原有的规则,使得函数体并不是return expression;形式的函数也可以使用返回类型推导。
为了诱发返回类型推导,函数声明必须将auto作为返回类型,但没有C++11的后置返回类型说明符:
auto DeduceReturnType(); //Return type to be determined.
如果函数实现中含有多个return表达式,这些表达式必须可以推断为相同的类型。
使用返回类型推导的函数可以被前向声明,但在定义之前不可以使用。它们的定义在使用它们的翻译单元之中必须是可用的。
这样的函数中可以存在递归,但递归调用必须在函数定义中的至少一个return语句之后,例如:
auto Correct(int i)
{
if (i == 1)
return (i); // return type deduced as int
else
return (Correct(i-1)+i); // OK to call it now
}
但是下面这样写是错误的:
auto Wrong(int i)
{
if(i!=1)
return (Wrong(i - 1) + i); // Too soon to call this. No prior return statement.
else
return i; // return type deduced as int
}
另一种类型推断
C++11中增加了两种推断类型的方式。auto根据给出的表达式产生具有合适类型的变量。decltype可以计算给出的表达式的类型。但是,decltype和auto推断类型的方式是不同的。特别是,auto总是推断出非引用类型,就好像使用了std::remove_reference一样,而auto&&总是推断出引用类型。然而decltype可以根据表达式的结果类别和表达式的性质推断出引用或非引用类型:
int i;
int &&f();
auto x3a = i; // decltype(x3a) is int
decltype(i) x3d = i; // decltype(x3d) is int
autox 4a = i; // decltype(x4a) is int
decltype((i)) x4d=(i); //decltype(x4d) is int&
auto x5a = f(); //decltype(x5a) is int
decltype(f()) x5d =f(); // decltype(x5d) is int&&
C++14将增加decltype(auto)的语法。这将允许auto声明使用decltype对于给定表达式的推断规则。
decltype(auto)的语法也可以用于返回类型推导,只需使用decltype(auto)代替auto来诱发。
放松的constexpr限制
C++11引入了constexpr函数的概念,这样的函数可以在编译期执行。它们的返回值可以通过计算所需的常量表达式(如整型的模板参数)得出。然而C++11要求constexpr函数只含有一个将被返回的表达式(也可以还含有static_assert声明等其它语句,但允许的语句类型很少)。
C++14将放松这些限制。constexpr函数将可以含有以下内容:
- 任何声明,除了:
- static或thread_local变量。
- 没有初始化的变量声明。
- 条件分支语句if和switch。goto是不允许的。
- 所有的循环语句,包括基于范围的for循环。
- 表达式可以改变一个对象的值,如果该对象的生命期在常量表达式函数内开始。包括对有constexpr声明的任何非const非静态成员函数的调用。
对于调用非constexpr函数的限制仍然存在。所以如果使用基于范围的for循环,begin和end的重载必须自身是constexpr。值得注意的是,std::initializer_list在本地和全局都具有constexpr声明的begin/end函数。
此外,C++11指出,所有被声明为constexpr的非静态成员函数也隐含声明为const(即函数不能修改*this的值)。这点已经被删除,非静态成员函数可以为非const。然而,只有当对象的生命期在常量表达式求值中开始,非const的constexpr成员函数才可以修改类成员。
变量模板
在C++之前的版本中,模板可以是函数模板或类模板。C++14还也可以创建变量模板。模板的一般规则,包括特化都适用于变量模板的声明和定义。[3]
聚合体成员初始化
C++11新增member initializer,这是一个表达式,被应用到类作用域的成员上,如果构造函数没有初始化这个成员。聚合体的定义被改为明确排除任何含有member initializer的类,因此,他们不允许使用聚合初始化。
C++14将放松这一限制,这种类型也允许聚合初始化。如果花括号初始化列表不提供该参数的值,member initializer会初始化它。
运行期确定一维长度的数组
在C++11和之前的标准中,在堆栈上分配的数组被限制为拥有一个固定的、编译期确定的长度。C++14允许在堆栈上分配的一个数组的最后一维具有运行期确定的长度。
运行期确定长度的数组不可以作为对象的一部分,也不可以具有全局存储期,他们只能被声明为局部变量。运行期确定长度的数组也可以使用C++11的基于范围的for循环。
二进制字面值
C++14的数字可以用二进制形式指定。其格式使用前缀0b或0B。这样的语法也被Java、Python和D语言使用。
新的标准库特性
共享的互斥体和锁
Optional值
C++14增加了一个optional类型,其值可能存在或不存在。这一类型基于Boost的boost::optional类,而添加了C++11和C++14中的新特性,诸如移动和in-place构造。它不允许用在引用类型上。类被专门的设计为一个literal type(如果模板参数本身是一个literal type),因此,在必要的情况下含有constexpr构造函数。
关联容器中的异构查找
C++标准库定义了四个关联容器类。set和multiset允许使用者根据一个值在容器中查找对应的的同类型的值。map和multimap容器允许使用者指定键(key)和值(value)的类型,根据键进行查找并返回对应的值。然而,查找只能接受指定类型的参数,在map和multimap中是键的类型,而在set和multiset容器中就是值本身的类型。
C++14允许通过其他类型进行查找,只需要这个类型和实际的键类型之间可以进行比较操作。在operator<被重载的情况下,这允许一个键为std::string的map与一个键为const char* 的map进行比较。
为保证向后兼容性,这种异构查找只在相应关联容器的比较运算符允许的情况下有效。标准库类 std::less(set和map到默认比较算符)与std::greater也可以被用来进行异构查找。
标准自定义字面值
C++11定义了自定义字面值的语法,但标准库没有使用其中的任何一个。C++14增加了以下标准字面值:
- "s",用于创建各种std::basic_string类型。
- "h"、"min"、"s"、"ms"、"us"、"ns",用于创建相应的std::chrono::duration时间间隔。
两个"s"互不干扰,因为表示字符串的只能对字符串字面值操作,而表示秒的只针对数字。
运行期确定长度的定长数组容器
std::vector类型表示一个可变长度的数组,可以根据需要增长和收缩。C++11的std::array表示一个固定长度的数组,其长度在编译时确定。
C + +14将添加std::dynarray类型,它代表一个固定长度的数组,其大小在运行期构造对象时确定。std::dynarray类被明显地设计为当它被放置在栈上时(直接放置在栈上,或作为另一个栈对象的成员),可以使用栈内存而不是堆内存。
通过类型寻址多元组
C++11引入的std::tuple类型允许不同类型的值的聚合体用编译期整型常数索引。C++14还允许使用类型代替常数索引,从多元组中获取对象。若多元组含有多于一个这个类型的对象,将会产生一个编译错误:
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对象。
std::integral_constant将会具有一个返回常量值的operator()重载。
全局std::begin/std::end函数之外,将会增加std::cbegin/std::cend函数,它们返回一个范围的开始/结束常量迭代器。