C++14 新特性

2023-11-10

一、新的语言特性

1 、泛型的 Lambda 函数

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

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

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

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

auto lambda = unnamed_lambda();

2 、Lambda 捕获表达式

C++11 的 lambda 函数通过值拷贝(by copy)或引用(by reference)捕获(capture)已在外层作用域声明的变量。这意味着 lambda 的值成员不可以是 move-only 的类型。C++14允许被捕获的成员用任意的表达式初始化。这既允许了 capture by value-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。第一次使用声明了一个新的变量,但在捕获部分,这个变量还不在作用域内。所以第二个 ptr 表示之前在 lambda 之外声明的变量。

3 、函数返回类型推导

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

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

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

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

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

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

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
}

4 、另一种类型推断

C++11 中有两种推断类型的方式。auto 根据给出的表达式产生具有合适类型的变量。decltype 可以计算给出的表达式的类型。但是,decltype 和 auto 推断类型的方式是不同的。

特别地,auto 总是推断出非引用类型,就好像使用了 std::remove_reference 一样,而 auto&&总是推断出引用类型。然而 decltype 可以根据表达式的值类别(value category)和表达式的性质推断出引用或非引用类型:

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)的语法。这允许不必显式指定作为 decltype 参数的表达式,而使用 decltype 对于给定表达式的推断规则。

decltype(auto)的语法也可以用于返回类型推导,只需用 decltype(auto)代替 auto。

5 、放松的 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成员函数才可以修改类成员。

6 、变量模板

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

7 、聚合体成员初始化

C++11 新增 member initializer,这是一个表达式,被应用到类作用域的成员上,如果构造函数没有初始化这个成员。聚合体的定义被改为明确排除任何含有 member initializer 的类,因此,他们不允许使用聚合初始化。C++14 将放松这一限制,这种类型也允许聚合初始化。如果花括号初始化列表不提供该参数的值,member initializer 会初始化它。

8 、二进制字面值

C++14 的数字可以用二进制形式指定。其格式使用前缀 0b 或 0B。这样的语法也被 Java、Python、Perl 和 D 语言使用。

9 、数字分位符

C++14 引入单引号(’)作为数字分位符号,使得数值型的字面量可以具有更好的可读性。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;

三、新的标准库特性

1 、共享的互斥体和锁

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

2 、元函数的别名

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 提供了更便捷的写法。其命名规则是:如果标准库的某个类模板(假设为 std::some_class)只含有唯一的成员,即成员类型 type,那么标准库提供std::some_class_t作为 typename std::some_class::type 的别名。在 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&);

3 、关联容器中的异构查找

C++标准库定义了四个关联容器类。set 和 multiset 允许用户根据一个值在容器中查找对应的的同类型的值。map 和 multimap 容器允许用户指定键(key)和值(value)的类型,根据键进行查找并返回对应的值。然而,查找只能接受指定类型的参数,在 map 和 multimap中是键的类型,而在 set 和 multiset 容器中就是值本身的类型。C++14 允许通过其他类型进行查找,只需要这个类型和实际的键类型之间可以进行比较操作。[17]这允许 std::setstd::string使用 const char*,或任何可以通过 operator< 与 std::string比较的类型作为查找的参数。为保证向后兼容性,这种异构查找只在提供给关联容器的比较器允许的情况下有效。标
准库的泛型比较器,如 std::less<>与 std::greater<>允许异构查找。

4 、标准自定义字面值

C++11 增加了自定义字面量(user-defined literals)的特性,使用户能够定义新的字面量后缀,但标准库并没有对这一特性加以利用。C++14 标准库定义了以下字面量后缀:
 “s”,用于创建各种 std::basic_string 类型。
 “h”、“min”、“s”、“ms”、“us”、“ns”,用于创建相应的 std::chrono::duration 时间间隔。

using namespace std::literals;
std::string str = "hello world"s;
std::chrono::seconds dur = 60s;

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

5 、通过类型寻址多元组

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

6 、较小的标准库特性

std::make_unique 可以像 std::make_shared 一样使用,用于产生 std::unique_ptr 对象。
std::is_final,用于识别一个 class 类型是否禁止被继承。
std::integral_constant 增加了一个返回常量值的 operator()。
全局 std::begin/std::end 函数之外,增加了 std::cbegin/std::cend 函数,它们总是返回常量迭代器(constant iterators)。

四、已被移除或是不包含在 C++14 标准的特性

1 、关于数组的扩展

在 C++11 和之前的标准中,在堆栈上分配的数组被限制为拥有一个固定的、编译期确定的长度。这一扩展允许在堆栈上分配的一个数组的最后一维具有运行期确定的长度。运行期确定长度的数组不可以作为对象的一部分,也不可以具有全局存储期,他们只能被声明为局部变量。运行期确定长度的数组也可以使用 C++11 的基于范围的 for 循环。

同时还将添加 std::dynarray 类型,它拥有与 std::vector 和 std::array 相似的接口。代表一个固定长度的数组,其大小在运行期构造对象时确定。std::dynarray 类被明显地设计为当它被放置在栈上时(直接放置在栈上,或作为另一个栈对象的成员),可以使用栈内存而是堆内存。

2 、Optional 值

类似于 C#中的可空类型,optional 类型可能含有或不含有一个值。这一类型基于 Boost的 boost::optional 类,而添加了 C++11 和 C++14 中的新特性,诸如移动和 in-place 构造。它不允许用在引用类型上。这个类被专门的设计为一个 literal type(如果模板参数本身是一个literal type),因此,它在必要的情况下含有 constexpr 构造函数。

3 、Concepts Lite

被 C++11 拒绝后,Concepts 受到彻底的修改。Concepts Lite 是 Concepts 的一个部分,仅包含类型约束,而不含 concept_map 和 axiom。它将在一个单独的 Technical Specification中发展,并有可能加入 C++17。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++14 新特性 的相关文章

随机推荐

  • Python全栈工程师-第15周-韦玮-专题视频课程

    Python全栈工程师 第15周 382人已学习 课程介绍 Python全栈工程师 第15周 课程收益 Python全栈工程师 讲师介绍 韦玮 更多讲师课程 企业家 IT讲师 讲师 作家 畅销书 精通Python网络爬虫 作者 CSDN特约
  • 【组合字符和预组字符】

    什么是组合字符 UCS里有些编码点分配给了 组合字符 它们类似于打字机上的无间隔重音键 单个的组合字符不是一个完整的字符 它是一个类似于重音符或其他指示标记 加在前一个字符后面 因而 重音符可以加在任何字符后面 那些最重要的被加重的字符 就
  • error:该操作无法完成,因为DbContext已被释放

    出现这个问题的可能原因是 private readonly Repository
  • 2021-07-14

    提取的权重的整理 在上一篇文章中 介绍了从神经网络中提取权重的步骤 本节主要介绍对于从神经网络中提取出的权重的整理方法 以适应将权重输入到嵌入式程序中的要求 目录 提取的权重的整理 一 使用Excel进行整理 1 将在pycharm中打印出
  • 【C++学习第十一讲】C++数据类型

    文章目录 一 编程语言中的数据类型 1 1 整型 Integer 1 2 浮点型 Floating Point 1 3 字符型 Character 1 4 布尔型 Boolean 1 5 数组 Array 1 6 字符串 String 1
  • alter database open oracle,alter database open resetlogs

    关于alter database open resetlogs及incarnation的一点理解 不完全恢复只能做一次吗 采用rman的默认设置 对数据库进行了backup database备份 进行了一些操作后 然后直接关闭启动到moun
  • JS下载网页为PDF

    1 准备引入包
  • FinalShell连接VM出现channel is not opened.

    问题描述 用finalshell连接虚拟机的服务器时显示连接成功 之后马上出现channel is not opened 解决方案 打开虚拟机 找到相关配置文件 cd etc ssh 更改配置 vi sshd config 找到被注释的 U
  • linux下SD卡mount的问题

    最近发现嵌入式开发板上 有的SD卡 8G SDHC 可以mount 有的则失败 2G SD 仔细看log信息 发现有如下区别 mount成功时 mmc0 host does not support reading read only swi
  • 【JavaScript】利用JS实现柱形统计图

    CSS代码部分
  • 嵌入式(信号机制)

    信号机制 概念 信号是在软件层次上对中断机制的一种模拟 是一种异步通信方式 所有信号的产生及处理全部都是由内核完成的 信号的产生 1 按键产生 2 系统调用函数产生 比如raise kill 3 硬件异常 4 命令行产生 kill 5 软件
  • 拟合工具箱的几个误差参数说明

    使用过Matlab的拟合 优化和统计等工具箱的网友 会经常遇到下面几个名词 SSE 和方差 误差平方和 The sum of squares due to errorMSE 均方差 方差 Mean squared errorRMSE 均方根
  • IDEA打开启动Vue项目和Vue文件

    一般前端Vue都是用VsCode专门用来编辑 今天突发奇想想用IDEA去编辑 首先把项目从git下拉下来在IDEA中打开 在IDEA打开终端运行相关命令 打开终端 也可以通过alt F12快捷键 终端如下 自行根据需要输入命令即可 相关代码
  • SMB、FTP、DNS、等六个服务总结

    一 SMB服务 1 SMB服务功能 不同系统主机之间实现文件 打印机等资源共享 2 SMB服务主配置文件路径 etc samba smb conf 3 SMB服务启动 重启 停止 方法 service smb start restart s
  • 如何使用docker和docker-compose在本地Testnet上开发EOS区块链

    EOS区块链的开发并不是立竿见影的 因为需要一些非显而易见的组件 需要对它们进行配置和协同工作 nodeos 块生成器守护程序 keosd 钱包守护进程 存储私钥 eosio cpp 智能合约编译器 eosio token 平台的参考标记
  • chapter1 静态分析技术-08PE文件分析 PEview

    1 下载PEview peview exe下载 peview exe绿色版 peview exev0 9 8 0绿色中文版 华军软件园https www onlinedown net soft 977166 htm 2 解压后打开PEvie
  • OTL、OCL、BTL电路及其判断方法(转)

    OTL OCL BTL电路及其判断方法 OTL Output Transformer Less 电路 称为无输出变压器功放电路 是一种输出级与扬声器之间采用电容耦合而无输出变压器的功放电路 它是高保真功率放大器的基本电路之一 但输出端的耦合
  • 鱼和熊掌不可兼得:Spring boot3,Swagger3(使用Mybatis-Plus搭建框架)

    文章写于2023年7月1日 目前使用的配置尽量用最新的 如果晚于这个日期很久 请参考新的配置 使用MyBatis plus搭建框架后使用swagger或者采用的Spring 3后使用swagger 3 启动项目出现如下错误 Type jav
  • 计算机视觉(二):图像检索以及基于图像描述符的搜索

    1 引言 在图像识别中 我们通常将图片的特征提取出来 并使用这些主要特征来进行识别 在OpenCV中提供了许多特征检测算法 下面让我们来学习一下怎么使用这些算法 2 特征定义 粗略的讲 特征就是有意义的图像区域 该区域具有独特性或易于识别性
  • C++14 新特性

    一 新的语言特性 1 泛型的 Lambda 函数 在 C 11 中 lambda 函数的形式参数需要被声明为具体的类型 C 14 放宽了这一要求 允许 lambda 函数的形式参数声明中使用类型说明符 auto auto lambda au