C++14C++14是C++的现行标准的非正式名称,正式名称为"International Standard ISO/IEC 14882:2014(E) Programming Language C++"。C++14旨在作为C++11的一个小扩展,主要提供漏洞修复和小的改进。C++14标准的委员会草案(Committee Draft)N3690于2013年5月15日发表。[1]工作草案(Working Draft)N3936已于2014年3月2日完成。最终的投票期结束于2014年8月15日,结果(一致通过)已于8月18日公布。[2] 新的语言特性以下为在C++14中被加入语言核心的特性。 泛型的lambda在C++11中,lambda函数的形式参数需要被声明为具体的类型。C++14放宽了这一要求,允许lambda函数的形式参数声明中使用类型说明符 auto lambda = [](auto x, auto y) {return x + y;}
泛型lambda函数遵循模板参数推导的规则。以上代码的作用与下面的代码相同:[4] 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函数通过值拷贝(by copy)或引用(by reference)捕获(capture)已在外层作用域声明的变量。这意味着lambda捕获的变量不可以是move-only的类型。[5] C++14允许lambda成员用任意的被捕获表达式初始化。这既允许了capture by value-move,也允许了任意声明lambda的成员,而不需要外层作用域有一个具有相应名字的变量。[6]这称为广义捕获(Generalized capture)。[7]即在捕获子句(capture clause)中增加并初始化新的变量,该变量不需要在lambda表达式所处的闭包域(enclosing scope)中存在;即使在闭包域中存在也会被新变量覆盖(override)。新变量类型由它的初始化表达式推导。用途是可以从外层作用域中捕获只供移动的变量并使用它。 也即,允许创建lambda捕获并用任意表达式初始化,捕获中的名字不需要一定出现在闭包域,初始化表达式在lambda被创建(而不是被调用)时求值。 使用引用捕获可以对被引用的外部闭包的变量使用别名。 auto x = 1;
auto f = [&r = x, x = x * 10] {
++r;
return r + x;
};
f(); // sets x to 2 and returns 12
这是通过使用一个初始化表达式完成的: auto lambda = [value = 1] {return value;}
lambda函数 使用标准函数 auto ptr = std::make_unique<int>(10); //See below for std::make_unique
auto lambda = [value = std::move(ptr)] {return *value;}
函数返回类型推导C++11允许lambda函数根据return语句的表达式类型推断返回类型。C++14为一般的函数也提供了这个能力。C++14还拓展了原有的规则,使得函数体并不是 为了启用返回类型推导,函数声明必须将 auto DeduceReturnType(); //返回类型由编译器推断
如果函数实现中含有多个return语句,这些表达式必须可以推断为相同的类型。[9] 使用返回类型推导的函数可以前向声明,但在定义之前不可以使用。它们的定义在使用它们的翻译单元(translation unit)之中必须是可用的。 这样的函数中可以存在递归,但递归调用必须在函数定义中的至少一个return语句之后:[9] 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中有两种推断类型的方式。 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增加了 const int x = 0;
auto x1 = x; // int
decltype(auto) x2 = x; // const int
int y = 0;
int& y1 = y;
auto y2 = y1; // int
decltype(auto) y3 = y1; // int&
int&& z = 0;
auto z1 = std::move(z); // int
decltype(auto) z2 = std::move(z); // int&&
// Return type is `int`.
auto f(const int& i) {
return i;
}
// Return type is `const int&`.
decltype(auto) g(const int& i) {
return i;
}
int CPP()
{
// Note: Especially useful for generic code!
int x = 123;
static_assert(std::is_same<const int&, decltype(f(x))>::value == 0);
static_assert(std::is_same<int, decltype(f(x))>::value == 1);
static_assert(std::is_same<const int&, decltype(g(x))>::value == 1);
return 0;
}
放松的constexpr函数限制C++11引入了声明为constexpr的函数的概念。声明为constexpr函数的意义是:如果其参数均为合适的编译期常量,则对这个constexpr函数的调用就可用于期望常量表达式的场合(如模板的非类型参数,或枚举常量的值)。如果参数的值在运行期才能确定,或者虽然参数的值是编译期常量,但不符合这个函数的要求,则对这个函数调用的求值只能在运行期进行。
然而C++11要求constexpr函数只含有一个将被返回的表达式(也可以还含有 C++14放松了这些限制。声明为constexpr的函数可以含有以下内容:[8]
此外,C++11指出,所有被声明为 变量模板
在C++之前的版本中,模板可以是函数模板或类模板(C++11引入了类型别名模板)。C++14现在也可以创建变量模板。在提案中给出的示例是变量 template<typename T>
constexpr T pi = T(3.141592653589793238462643383);
// 适用于特化规则 :
template<>
constexpr const char* pi<const char*> = "pi";
聚合类成员初始化C++11增加了default member initializer,如果构造函数没有初始化某个成员,并且这个成员拥有default member initializer,就会用default member initializer来初始化成员。聚合类(aggregate type)的定义被改为明确排除任何含有default member initializer的类类型,因此,如果一个类含有default member initializer,就不允许使用聚合初始化。 C++14放松了这一限制,[8]含有default member initializer的类型也允许聚合初始化。如果在定义聚合体类型的对象时,使用的花括号初始化列表没有指定该成员的值,将会用default member initializer初始化它。[12] struct CXX14_aggregate {
int x;
int y = 42;
};
CXX14_aggregate a = {1}; // C++14允许。a.y被初始化为42
二进制字面量C++14的数字可以用二进制形式指定。[8]其格式使用前缀 数字分位符C++14引入单引号(')作为数字分位符号,使得数值型的字面量可以具有更好的可读性。[13] Ada、D语言、Java、Perl、Ruby等程序设计语言使用下划线(_)作为数字分位符号,C++之所以不和它们保持一致,是因为下划线已被用在用户自定义的字面量的语法中。 auto integer_literal = 100'0000;
auto floating_point_literal = 1.797'693'134'862'315'7E+308;
auto binary_literal = 0b0100'1100'0110;
auto silly_example = 1'0'0'000'00;
deprecated 属性
[[deprecated]] int f();
[[deprecated("g() is thread-unsafe. Use h() instead")]]
void g( int& x );
void h( int& x );
void test() {
int a = f(); // 警告:'f'已弃用
g(a); // 警告:'g'已弃用:g() is thread-unsafe. Use h() instead
}
新的标准库特性共享的互斥体和锁C++14增加了一类共享的互斥体和相应的共享锁[14][15]。起初选择的名字是 元函数的别名C++11定义了一组元函数,用于查询一个给定类型是否具有某种特征,或者转换给定类型的某种特征,从而得到另一个类型。后一种元函数通过成员类型type来返回转换后的类型,当它们用在模板中时,必须使用typename关键字,这会增加代码的长度。 template <class T>
type_object<
typename std::remove_cv<
typename std::remove_reference<T>::type
>::type
>get_type_object(T&);
利用类型别名模板,C++14提供了更便捷的写法。其命名规则是:如果标准库的某个类模板(假设为 在C++14,拥有类型别名的元函数包括:remove_const、remove_volatile、remove_cv、add_const、add_volatile、add_cv、remove_reference、add_lvalue_reference、add_rvalue_reference、make_signed、make_unsigned、remove_extent、remove_all_extents、remove_pointer、add_pointer、aligned_storage、aligned_union、decay、enable_if、conditional、common_type、underlying_type、result_of、tuple_element。 template <class T>
type_object<std::remove_cv_t<std::remove_reference_t<T>>>
get_type_object(T&);
关联容器中的异构查找C++标准库定义了四个关联容器类。set和multiset允许使用者根据一个值在容器中查找对应的同类型的值。map和multimap容器允许使用者指定键(key)和值(value)的类型,根据键进行查找并返回对应的值。然而,查找只能接受指定类型的参数,在map和multimap中是键的类型,而在set和multiset容器中就是值本身的类型。 C++14允许通过其他类型进行查找,只需要这个类型和实际的键类型之间可以进行比较操作。[17]这允许 为保证向后兼容性,这种异构查找只在提供给关联容器的比较器允许的情况下有效。标准库的泛型比较器,如 标准自定义字面量C++11增加了自定义字面量(user-defined literals)的特性,使用户能够定义新的字面量后缀,但标准库并没有对这一特性加以利用。C++14标准库定义了以下字面量后缀:[17]
这些字面量可用于编译时的constexpr。 auto str = "hello world"s; // 自动推导为 std::string
auto dur = 60s; // 自动推导为 chrono::seconds
auto z = 1i; // 自动推导为 complex<double>
两个"s"互不干扰,因为表示字符串的只能对字符串字面量操作,而表示秒的只针对数字。[19] 通过类型寻址多元组C++11引入的 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
较小的标准库特性
全局 已被移除或是不包含在C++14标准的特性因为C++14的主要目的是漏洞修复和小的改进,一些重量级的特性被从C++14中移除,其中有部分将加入C++17标准。 关于数组的扩展在C++11和之前的标准中,在堆栈上分配的数组被限制为拥有一个固定的、编译期确定的长度。这一扩展允许在堆栈上分配的一个数组的最后一维具有运行期确定的长度。[6] 运行期确定长度的数组不可以作为对象的一部分,也不可以具有全局存储期,他们只能被声明为局部变量。运行期确定长度的数组也可以使用C++11的基于范围的for循环。[21] 同时还将添加 由于一些设计无法达成一致,这一扩展已被放弃。 Optional值类似于C#中的可空类型,optional类型可能含有或不含有一个值。这一类型基于Boost的 Concepts Lite被C++11拒绝后,Concepts受到彻底的修改。Concepts Lite是Concepts的一个部分,仅包含类型约束,而不含 另见参考资料
|