C++14学习记录:新语言功能特性
- 本篇笔记汇总了C++14中的主要新语言功能特性,根据个人理解与查阅的资料进行记录。
- 主要参考地址:cppreference
- C++14主要是在C++11标准之上的一些补充,所以相对的内容较少一些。
目录
- · 变量模板
- · 泛型 lambda
- · lambda 初始化捕获
- · new/delete 消除
- · constexpr 函数上放松的限制
- · 二进制字面量
- · 数位分隔符
- · 函数的返回类型推导
- · 带默认成员初始化器的聚合类。
· 变量模板
在之前的版本中,模板均为函数模板或者类模板,而从C++14起,变量也可以使用模板了。变量模板的语法为 template < 形参列表 > 变量声明
,通常模板的规则都适用于变量模板,比如说特化什么的。下面是几个例子:
template<class T>
constexpr T pi = T(3.1415926535897932385L);// 变量模板template<class T>
T circular_area(T r)//函数模板
{return pi<T> * r * r; //pi<T> 是变量模板实例化
}int main()
{double a = 3;cout << circular_area(a) << endl;cout << circular_area<double>(2) << endl;return 0;
}输出:
28.2743
12.5664
与其他静态成员一样,静态数据成员模板的需要一个定义。这种定义可以在类定义外提供。处于命名空间作用域的静态数据成员的模板声明也可以是类模板的非模板数据成员的定义:
struct limits
{template<typename T>static const T min;//静态数据成员模板的声明
};
template<typename T>
const T limits::min = { };//静态数据成员模板的定义template<class T>
class X
{static T s;//类模板的非模板静态数据成员的声明
};
template<class T>
T X<T>::s = 0;//类模板的非模板静态数据成员的定义
在C++14引入变量模板前,参数化变量通常实现为类模板的静态数据成员,或返回所需值的 constexpr 函数模板,这点在STL源码里可以经常看见。但是说实话可能变量模板这个新特性比较新或者说非他不可的场景比较少,暂时我还没有用到过或者说在源码里见到过这个东西。
· 泛型 lambda
在C++11中,lambda
表达式的形参需要被声明为具体的类型。而在C++14中其放宽了这一要求,这就使得 lambda
表达式的形参声明中可以使用类型说明符 auto
,从而达成泛型lambda的目的。
int main()
{auto lambda = [](auto x, auto y){return x + y;};cout << lambda(1.1, 2) << endl;cout << lambda(1, 2) << endl;cout << lambda('a', 'b') << endl;return 0;
}输出:
3.1
3
195
泛型 lambda
表达式遵循模板参数推导的规则。上面这段代码中的泛型 lambda
表达式的作用与下面的代码相同:
struct unnamed_lambda
{template<typename T, typename U>auto operator()(T x, U y) const{return x + y;}
};
auto lambda = unnamed_lambda();
这东西我感觉使用起来还是挺方便的,毕竟某些场景泛型 lambda
会节省很多功夫。不过就我而言需求也没那么强,因为我个人用到 lambda
的场景一般对于 lambda
的传参都已经有明确的定义了,这时就没什么泛型编程的需求。
· lambda 初始化捕获
C++11的 lambda
表达式通过值拷贝和引用来捕获在外层作用域声明的变量。这就意味着 lambda
的值成员不可以是 move-only
类型,比如说智能指针 std::unique_ptr
,它就是个仅移动的类型。而在C++14中,lambda
表达式放开限制,允许对被捕获的成员用任意的表达式初始化。
这样放开限制的第一个好处就是可以通过 std::move
来初始化捕获一个 move-only
类型的变量,如下例:
int main()
{unique_ptr<int>p1 = make_unique<int>(10);cout << "1. main() p1:" << *p1 << endl;auto lambda = [p1 = move(p1)]()//此处的第一个p1声明了新的变量,而第二个p1则表示在lambda之外声明的变量{cout << "2. lambda() p1:" << *p1 << endl;};lambda();if(!p1){cout << "3. main() p1 is empty" << endl;}return 0;
}输出:
1. main() p1:10
2. lambda() p1:10
3. main() p1 is empty
而第二个好处就是可以任意声明 lambda
的值成员,而不需要外层作用域有一个具有相应名字的变量,如下例:
int main()
{auto lambda = [a = 12345](){cout << "lambda() a:" << a << endl;};lambda();return 0;
}输出:
lambda() a:12345
这个新特性我觉得挺有用的,让 lambda
更加的灵活和方便,虽然我还没用过。但是有一点需要注意,声明 lambda
的时候会直接执行里面的捕获语句,所以被捕获的 move-only
类型变量会直接失效…比如下面这个,即使不执行 lambda
函数也会失效。
int main()
{unique_ptr<int>p1 = make_unique<int>(10);cout << "1. main() p1:" << *p1 << endl;auto lambda = [p1 = move(p1)]()//此处的第一个p1声明了新的变量,而第二个p1则表示在lambda之外声明的变量{┊ cout << "2. lambda() p1:" << *p1 << endl;};//lambda();if(!p1){┊ cout << "3. main() p1 is empty" << endl;}return 0;
}输出:
1. main() p1:10
3. main() p1 is empty
· new/delete 消除
贴一个stackoverflow上的相关回答(渣翻): 点我跳转
问:
鉴于make_unique
和make_shared
的可用性,以及unique_ptr
和shared_ptr
的自动析构函数,在C++14中使用new
和delete
的情况是什么(除了支持遗留代码)?答:
虽然在许多情况下智能指针比原始指针更可取,但new/delete
在 C++14 中仍然有很多用例。
如果您需要编写任何需要就地构建的内容,例如:
1.一个内存池
2.一个分配器
3.标记变体
4.到缓冲区的二进制消息
您将需要使用new
和delete
。
以及对于一些需要编写的容器,您可能希望使用原始指针进行存储。
即使对于标准的智能指针,如果您想使用自定义删除器,您仍然需要new
,因为make_unique
和make_shared
不允许这样做。
· constexpr 函数上放松的限制
C++11引入了声明为 constexpr
的函数的概念。声明为 constexpr
函数的意义是:如果其参数均为合适的编译期常量,则对这个 constexpr
函数的调用就可用于期望常量表达式的场合(如模板的非类型参数,或枚举常量的值)。如果参数的值在运行期才能确定,或者虽然参数的值是编译期常量,但不符合这个函数的要求,则对这个函数调用的求值只能在运行期进行。然而C++11要求 constexpr
函数只含有一个将被返回的表达式(也可以还含有 static_assert
声明等其它语句,但允许的语句类型很少)。
在C++14中将放松这些限制,但 goto
仍然不允许在 constexpr
函数中出现,其中可以含有以下内容:
- 任何声明,除了:
1.static
或thread_local
变量。
2.没有初始化的变量声明。 - 条件分支语句
if
和switch
。 - 所有的循环语句,包括基于范围的
for
循环。 - 表达式可以改变一个对象的值,只需该对象的生命期在声明为
constexpr
的函数内部开始。包括对有constexpr
声明的任何非const
非静态成员函数的调用。
此外,C++11指出,所有被声明为 constexpr
的非静态成员函数也隐含声明为 const
(即函数不能修改*this的值)。这点已经被删除,非静态成员函数可以为非 const
。
怎么说呢,这东西在标准库里挺常见的,因为标准库要进行泛型编程,用模板和 constexpr
关键字的频率很高。但是在我个人的编程中,感觉用 constexpr
修饰的函数比较少吧…所以还没咋用过这种复杂点的 constexpr
函数。
· 二进制字面量
C++14的数字可以使用二进制形式指定,其格式使用前缀 0b
或 0B
,类似之前的十六进制前缀 0x
。没啥好说的。
int main()
{int x = 0x666;int y = 0b101;cout << x << endl << y << endl;return 0;
}输出:
1638
5
· 数位分隔符
C++14引入单引号 '
作为数字分位符号,使得数值型的字面量可以具有更好的可读性。
Ada、D语言、Java、Perl、Ruby等程序设计语言使用下划线 _
作为数字分位符号,C++之所以不和它们保持一致,是因为下划线已被用在用户自定义字面量的语法中。
int main()
{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;cout << integer_literal << endl << floating_point_literal << endl;cout << binary_literal << endl << silly_example << endl;return 0;
}输出:
1000000
1.79769e+308
1222
10000000
这东西我觉得挺有用的,由上面的例子可以看出来这个 '
是随意加的,编译器会忽略它,所以可以按自己的喜好或者约定来添加分隔符。
· 函数的返回类型推导
C++11允许 lambda
表达式根据 return
语句的表达式类型推断返回类型。C++14为一般的函数也提供了这个能力,此外C++14还拓展了原有的规则,使得函数体并不是 {return expression;}
形式的函数也可以使用返回类型推导。
为了启用返回类型推导,函数声明必须将 auto
作为返回类型,但没有C++11的后置返回类型说明符:
auto DeduceReturnType(); //返回类型由编译器推断
如果函数实现中含有多个 return
语句,这些表达式必须可以推断为相同的类型。使用返回类型推导的函数可以前向声明,但在定义之前不可以使用。它们的定义在使用它们的翻译单元(translation unit)之中必须是可用的。这样的函数中可以存在递归,但递归调用必须在函数定义中的至少一个 return
语句之后:
auto Correct(int i)
{if (i == 1)return i;//返回类型被推断为intelsereturn Correct(i-1)+i;//正确,可以调用
}auto Wrong(int i)
{if(i != 1)return Wrong(i-1)+i;//不能调用,之前没有return语句elsereturn i;//返回类型被推断为int
}
这是我个人认为此次更新最有用的一个内容了,auto
返回值可以节省很多功夫。不过坏处就是不太明了,类型比较复杂的时候可读性比较差,但是我觉得一般的小场景用用还是很舒服的。
· 带默认成员初始化器的聚合类。
C++11新增 member initializer
,这是一个表达式,被应用到类作用域的成员上,如果构造函数没有初始化这个成员。聚合体的定义被改为明确排除任何含有 member initializer
的类,因此,他们不允许使用聚合初始化。
C++14将放松这一限制,这种类型也允许聚合初始化。如果花括号初始化列表不提供该参数的值,member initializer
会初始化它。
C++14学习记录:新语言功能特性相关推荐
- 如何学习一门新语言(针对初学者)
程序员之道,万变不离其宗,说相声讲究的是说.学.逗.唱,学习程序最快也是最好的办法就是:过.抄.仿.改.调.看.练.创.悟: 1.过: 学习一门新的语言,第一步就是把它所涉及的基础知识大体过一点,不求 ...
- 学习记录_C语言打砖块小游戏
第一次体验到了写代码一上午,修BUG修一天的感觉.但还有一个BUG始终不知道如何解决,那便是如果额外产生的小球触碰到礼盒砖块(会产生额外球),便会在左上角出现隐形球.无奈只好将礼盒砖块改为一个,并将存 ...
- 学习记录-程序语言基础(1)
1.汇编程序的功能是将用汇编语言编写的源程序翻译成机器指令程序.他一般至少需要两次扫描源程序才能完成翻译过程. 2.编译程序也称编译器,功能是把用高级语言书写的源程序翻译成与之等价的目标程序.编译过程 ...
- 【入门必看】如何快速学一门新语言?
先说结论 我花了半个月时间学习Go,就开发了商业项目,抗住了并发考验,并且成功被Go圈粉. 2022年初,入职新公司后,花了3天时间,学习GoFrame框架,提前完成了开发任务,在项目复盘会上成了同事 ...
- (转)十步精通新语言
标题党一下..... 昨天做了一个特殊抓取的爬虫,不到50行python代码,痛痛快快的给我抓取了2000个flashgame和玩法说明.除了感叹会写程序实在太好了之外,还想到了如何快速学习一门新语言 ...
- C语言学习记录(14)英文单词排序 查找最长的单词
C语言学习记录 前言 一直自己没有学习做笔记的习惯,所以为了加强自己对知识的深入理解,决定将学习笔记写下来,希望向各位大牛们学习交流! 不当之处请斧正!在此感谢!这边就先从学习C语言写起,自己本身对程 ...
- Python|装饰器|执行时间|递归|动态属性|静态方法和类|继承和多态|isinstance类型判断|溢出|“魔法”方法|语言基础50课:学习记录(6)-函数的高级应用、面向对象编程、进阶及应用
文章目录 系列目录 原项目地址: 第16课:函数的高级应用 装饰器(记录执行时间的例子) 递归调用 简单的总结 第17课:面向对象编程入门 类和对象 定义类 创建和使用对象 初始化方法 打印对象 面向 ...
- 【C语言进阶深度学习记录】五 C语言中变量的属性
上一篇文章学习了C语言中的类型转换,点击链接查看:[C语言进阶深度学习记录]四 C语言中的类型转换. 文章目录 1 C语言的变量属性 1.1 auto关键字 1.2 register关键字 1.3 s ...
- R语言基础学习记录4:重要函数
时间: 2018-07-18(学习) 2018-07-22(学习记录) 教程:慕课网 <R语言基础> 讲师:Angelayuan 补充内容: R语言常用函数总结大全.gl()函数 学习内容 ...
最新文章
- Android高级大纲
- 优化算法optimization:AdaDelta
- 10岁对c语言特别感兴趣,山东男孩,8岁懂电脑编程,10岁考上南科大,最后为何惨遭退学?...
- python可以做什么系统-python能做哪方面的工作
- 鼠标移动区域局部放大
- [Servlet]深入掌握Servlet
- OJ7627-鸡蛋的硬度【各种dp之4】
- java 布局管理器_有时在Java中,一个布局管理器是不够的
- java 对线程进行事务控制_Java 多线程事务回滚 ——多线程插入数据库时事务控制...
- css按钮居中_你不一定知道的CSS最小和最大(宽度/高度)知识点及优缺点
- Oracle中的单行函数
- Kali Linux 网络扫描秘籍 第八章 自动化 Kali 工具
- 连接服务器_服务器海量TCP连接如何高效保活?
- Linux环境下编写C程序
- html 怎么在背景图片上添加内容,css 如何在一个背景图片的中间添加文字呢?
- VLN论文英语表达积累
- 网站服务器坏了要修多久,大学服务器电脑坏了,一分钟修好收500,朋友:有钱不挣是傻子!...
- Python安装libsvm
- 【测试与自动化】介绍-框架-Jest-覆盖率-异步代码-e2e-Vue测试
- 怎么用Q-Q图验证数据集的分布