1.auto

auto是旧关键字,在C++11之前,auto用来声明自动变量,表明变量存储在栈,很少使用。在C++11中被赋予了新的含义和作用,用于类型推断。

auto关键字主要有两种用途:一是在变量定义时根据初始化表达式自动推断该变量的类型,二是在声明或定义函数时作为函数返回值的占位符,此时需要与关键字decltype连用。

1.1用法示例

(1)auto用于推断变量类型示例。

  1. auto i = 42; //i is an int
  2. auto l = 42LL; //l is an long long
  3. auto p = new foo(); //p is a foo*

(2)声明或定义函数时作为函数返回值的占位符。

auto不能用来声明函数的返回值。但如果函数有一个尾随的返回类型时,auto是可以出现在函数声明中返回值位置。这种情况下,auto并不是告诉编译器去推断返回类型,而是指引编译器去函数的末端寻找返回值类型。在下面这个例子中,函数返回值类型是operator+操作符作用在T、U类型变量上的返回值类型。

  1. template<class T, class U> auto add(T t, U u) -> decltype(t + u){
  2. return t + u;
  3. }

2.decltype

decltype与auto关键字一样,用于进行编译时类型推导,不过它与auto还是有一些区别的。decltype的类型推导并不是像auto一样是从变量声明的初始化表达式获得变量的类型,而是总是以一个普通表达式作为参数,返回该表达式的类型,而且decltype并不会对表达式进行求值[2]。

2.1decltype推导规则

(1)如果e是一个变量或者类成员访问表达式,假设e的类型是T,那么的decltype(e)为T,decltype((e))为T&。

(2)如果e是一个解引用操作,那么decltype(e)和decltype((e))均为T&。

(3)否则decltype(e)与decltype((e))均为T。

2.2用法示例

(1)推导出表达式类型。

  1. struct A { double x; };
  2. const A* a = new A{0};
  3. //第一种情况
  4. decltype(a->x) y; // type of y is double
  5. decltype((a->x)) z = y; // type of z is const double&,因为a一个常量对象指针
  6. //第二种情况
  7. int* aa=new int;
  8. decltype(*aa) y=*aa; //type of y is int&,解引用操作
  9. //第三种情况
  10. decltype(5) y; //type of y is int
  11. decltype((5)) y; //type of y is int
  12. const int&& RvalRef() { return 1; }
  13. decltype ((RvalRef())) var = 1; //type of var is const int&&

(2)与using/typedef合用,用于定义类型。

  1. using size_t = decltype(sizeof(0));//sizeof(a)的返回值为size_t类型
  2. using ptrdiff_t = decltype((int*)0 - (int*)0);
  3. using nullptr_t = decltype(nullptr);
  4. vector<int >vec;
  5. typedef decltype(vec.begin()) vectype;
  6. for (vectype i = vec.begin; i != vec.end(); i++){
  7. //...
  8. }

显而易见,与auto一样,也提高了代码的可读性。

(3)泛型编程中结合auto,用于追踪函数的返回值类型,这也是decltype的最大用途。

  1. template <typename _Tx, typename _Ty>
  2. auto multiply(_Tx x, _Ty y)->decltype(x*y)
  3. {
  4. return x*y;
  5. }

3.nullptr

以前都是用0来表示空指针的,但由于0可以被隐式类型转换为整形,这就会存在一些问题。关键字nullptr是std::nullptr_t类型的值,用来指代空指针。nullptr和任何指针类型以及类成员指针类型的空值之间可以发生隐式类型转换,同样也可以隐式转换为bool型(取值为false)。但是不存在到整形的隐式类型转换[3]。

  1. int* p1 = NULL;
  2. //或
  3. int* p2 = nullptr;

4.constexpr

constexpr再C++11中用于申明常量表达式(const expression)。常量表达式是指值不会改变并且在编译过程中就得到计算结果的表达式[4]。

  1. const int i=3; //i是一个常量变量
  2. const int j=i+1; //j是一个常变量,i+1是一个常量表达式
  3. int k=23; //k的值可以改变,从而不是一个常变量
  4. const int m=f(); //m不是常变量,m的值只有在运行时才会获取。

一般来说,若果一旦认定变量是一个常量表达式,那就把它声明为constexpr类型。

必须明确一点,在constexpr声明中,如果定义了一个指针,限定符号constexpr仅仅对指针有效,与指针所指对象无关。

  1. const int *p=nullptr; //p是一个指向整型常量的指针(pointer to const)
  2. constexpr int *p1=nullptr; //p1是一个常量指针(const pointer)

5.noexcept

在C++11标准之前,C++在函数声明中有exception specification(异常声明)的功能,用来指定函数可能抛出的异常类型[5]。

  1. voidFunc0() throw(runtime_error);
  2. voidFunc1() throw();
  3. voidFunc2();

函数Func0可能抛出runtime_error类型的异常;函数Func1不会抛出任何异常;函数Func2没有异常说明,则该函数可以抛出任何类型的异常。

如果函数抛出了没有在异常说明中列出的异常,则编译器会调用标准库函数unexpected。默认情况下,unexpected函数会调用terminate函数终止程序。

这种异常声明的功能很少使用,因此在C++11中被弃用(实际仍可使用)。C++11引入noexcept,具有两层含义,一个是修饰符,而是操作符。具体用法如下。

(1)修饰符示例。

  1. voidFunc3() noexcept;

noexcept的功能相当于上面的throw(),表示函数不会抛出异常。如果noexcept修饰的函数抛出了异常,编译器可以选择直接调用std::terminate()终止程序运行。noexcept比throw()效率高一些。

  1. voidFunc4() noexcept(常量表达式);

如果常量表达式的结果为true,表示该函数不会抛出异常,反之则有可能抛出异常。不带常量表达式的noexcept相当于noexcept(true)。

(2)操作符示例。

上面noexcept的用法是其作为修饰符时的用法,实际上noexcept还可以作为操作符,常用于模板中。

  1. template <typename T> void func5() noexcept( noexcept(T()) ) {}

第2个noexcept就是一个操作符,如果其参数是一个有可能抛出异常的表达式,则返回值为false,那么func5有可能会抛出异常,否则返回值为true,func5为noexcept(true),不会抛出异常。

这样函数是否会抛出异常,可以由表达式进行推导,使得c++11更好的支持泛型编程。

6.final和override

2012 年 3 月 22 日,GCC 4.7.0 正式发布。从这个版本开始,GCC 增加了许多新的 C++ 11 的特性。今天我们要介绍的是其中的一个特性:显式地使用 final和override关键字[6]。

6.1final

(1)final用于修饰类。

final修饰类,可用于申明终结类。从此C++终于有申明终结类的关键字了。

  1. struct B1 final { };
  2. struct D1 : B1 { }; // 错误!不能从 final 类继承!

上面的代码是错误的,因为 D1 试图继承 B1,而 B1 被 final声明为终结类,类似于Java的关键字的作用。

(2)final用于修饰虚函数。

final用于修饰虚函数,表明子类不能重写该虚函数,为”终结虚函数“。例如:

  1. struct B2
  2. {
  3. virtual void f() final {} // final 函数
  4. };
  5. struct D2 : B2
  6. {
  7. virtual void f() {}
  8. };

这段代码会出错,因为D2::f重写了B2::f,但是B2::f却被声明为 final 。

6.2override

假如我们继承基类的虚函数,在重写虚函数时写错了,参数类型不对或个数不对,但是编译没问题,造成了对基类同名函数的隐藏,运行时候和设计的不一样,override就是辅助检查是否正真重写了继承的虚函数。例如:

  1. struct B3
  2. {
  3. virtual void f() {}
  4. };
  5. struct D3 : B3
  6. {
  7. void f() {}
  8. };

开发 D3 的程序员真的想重写B3::f函数吗?还是说,他只是不小心写了个与父类同名的函数,却在不经意间导致了隐藏?为了避免这种错误,C++ 11 引入了override关键字。于是,我们会发现,下面的一段代码是会出错的:

  1. struct B4
  2. {
  3. virtual void g(int) {}
  4. };
  5. struct D4 : B4
  6. {
  7. virtual void g(int) override {} // OK
  8. virtual void g(double) override {} // Error
  9. };

多亏了override关键字,我们可以让编译器帮我们检测到这个很难发现的程序错误。这段代码的错误在于,override关键字表明,g(double)虽然想要进行override的操作,但实际父类并没有这么个函数。在实际开发中,建议大家重写继承而来的虚函数时,加上关键字virtual表明当前函数式虚函数,C++编译器的”放纵“降低了代码的可读性。

值得注意的是,这些并不是一些语法糖,而是能确确实实地避免很多程序错误,并且暗示编译器可以作出一些优化。调用标记了final的virtual函数,例如上面的B2::f,GNU C++ 前端会识别出,这个函数不能被覆盖,因此会将其从类的虚表中删除。而标记为final的类,例如上面的 B1,编译器则根本不会生成虚表。这样的代码显然更有效率。

7.sizeof…运算符

sizeof…运算符的作用是获取C++11中可变参数模板中参数包中元素个数。类似sizeof,sizeof…返回一个常量表达式,而且不会对模板的实参求值[7]。例如:

  1. template<typename... Args> void g(Args... args){
  2. cout<<sizeof...(Args)<<endl; //类型参数的数目
  3. cout<<sizeof...(args)<<endl; //函数参数的数目
  4. }

8.default和delete[8]

8.1default

我们知道,C++98和C++03编译器在类中会隐式地产生四个函数:默认构造函数、拷贝构造函数、析构函数和赋值运算符函数,它们被称为特殊成员函数。在 C++11 中,被称为 “特殊成员函数” 的还有两个:移动构造函数和移动赋值运算符函数。如果用户申明了上面六种函数,编译器则不会隐式产生。C++引入的default关键字,可显示地、强制地要求编译器为我们生成默认版本。

  1. class DataOnly{
  2. public:
  3. DataOnly()=default; //default constructor
  4. ~DataOnly()=default; //destructor
  5. DataOnly(const DataOnly& rhs)=default; //copy constructor
  6. DataOnly& operator=(const DataOnly & rhs)=default; //copy assignment operator
  7. DataOnly(const DataOnly && rhs)=default; //C++11,move constructor
  8. DataOnly& operator=(DataOnly && rhs)=default; //C++11,move assignment operator
  9. };

上面的代码,就可以让编译器生成上面六个函数的默认版本。

8.2delete

delete关键在C++11之前是对象释放运算符,但在C++11中,被赋予了新的功能,主要有如下几种作用:

(1)禁止编译器生成上面六种函数的默认版本。

  1. class DataOnly{
  2. public:
  3. DataOnly()=delete; //default constructor
  4. ~DataOnly()=delete; //destructor
  5. DataOnly(const DataOnly& rhs)=delete; //copy constructor
  6. DataOnly& operator=(const DataOnly & rhs)=delete; //copy assignment operator
  7. DataOnly(const DataOnly && rhs)=delete; //C++11,move constructor
  8. DataOnly& operator=(DataOnly && rhs)=delete; //C++11,move assignment operator
  9. };

(2)C++11 中,delete 关键字可用于任何函数,不仅仅局限于类成员函数。在函数重载中,可用delete来滤掉一些函数的形参类型,如下:

  1. bool isLucky(int number); // original function
  2. bool isLucky(char) = delete; // reject chars
  3. bool isLucky(bool) = delete; // reject bools
  4. bool isLucky(double) = delete; // reject doubles and floats

这样在调用 isLucky 函数时,如果参数类型不对,则会出现错误提示

  1. if (isLucky('a'))... // error! call to deleted function
  2. if (isLucky(true))... // error!
  3. if (isLucky(3.5))... // error!

(3)在模板特例化中,也可以用 delete 来过滤一些特定的形参类型。例如,Widget 类中声明了一个函数模板,当进行模板特化时,要求禁止参数为 void* 的函数调用。

  1. class Widget {
  2. public:
  3. template<typename T> void processPointer(T* ptr){}
  4. };
  5. template<> void Widget::processPointer<void>(void*)=delete; //deleted function template

参考文献

[1]【C++11新特性】auto关键字

[2]C++11特性:decltype关键字

[3]C++开发者都应该使用的10个C++11特性

[4]constexpr与常量表达式(c++11标准)

[5][了解C++11(五)—— noexcept]{http://www.xuebuyuan.com/2069091.html}

[6]C++11 新特性:显式 override 和 final

[7]C++ primer中文版第五版:619-619

[8]C++11 之 delete 和 default

C++11——引入的新关键字的更多相关文章

  1. C++11特性:auto关键字

    前言 本文的内容已经不新鲜了.关于auto,翻来覆去被人知道的都是这些东西,本文并没有提出新颖的auto用法. 本人原是痛恨博客一篇篇都是copy而来缺乏新意的探索,当然,本文不是copy而来,但发布 ...

  2. 一起学习c++11——c++11中的新语法

    c++11新语法1: auto关键字 c++11 添加的最有用的一个特性应该就是auto关键字. 不知道大家有没有写过这样的代码: std::map<std::string, std::vect ...

  3. c++builder XE7 C++11 C++0x 新语法

    Non-static data member initializers 非静态成员变量初始化变得简单,这个XE7 64位编译成功,32位还是不支持 As a simple example, struc ...

  4. Mysql8.0.11简介,新特性

    MySQL 8.0 正式版 8.0.11 已发布,官方表示 MySQL 8 要比 MySQL 5.7 快 2 倍,还带来了大量的改进和更快的性能! 注意:从 MySQL 5.7 升级到 MySQL 8 ...

  5. kafka 幂等生产者及事务(kafka0.11之后版本新特性)

    1. 幂等性设计1.1 引入目的生产者重复生产消息.生产者进行retry会产生重试时,会重复产生消息.有了幂等性之后,在进行retry重试时,只会生成一个消息. 1.2 幂等性实现1.2.1 PID ...

  6. C++11 override 和 final 关键字

    C++11之前,一直没有继承控制关键字.禁用一个类的进一步衍生是可能的但也很棘手.为避免用户在派生类中重载一个虚函数,你不得不向后考虑. C++ 11添加了两个继承控制关键字:override和fin ...

  7. 利用ABAP 740的新关键字REDUCE完成一个实际工作任务

    ABAP 740从2013年发布至今已经过去很长的时间了,下面这张图来自SAP社区博客: ABAP News for Release 7.40 – What is ABAP 7.40? 图中的ABAP ...

  8. C# 9.0中引入的新特性init和record的使用思考

    写在前面 .NET 5.0已经发布,C# 9.0也为我们带来了许多新特性,其中最让我印象深刻的就是init和record type,很多文章已经把这两个新特性讨论的差不多了,本文不再详细讨论,而是通过 ...

  9. (数据科学学习手札139)geopandas 0.11版本重要新特性一览

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 大家好我是费老师,就在几天前,geopandas ...

随机推荐

  1. WPF获取窗口句柄

    通过WPF的互操作帮助类WindowInteropHelper,相关连接:https://msdn.microsoft.com/zh-cn/library/system.windows.interop ...

  2. 利用 Intel Realsense做SLAM开发(一)

    最近手里拿到一台Realsense D435,就是这个: https://click.intel.com/intelr-realsensetm-depth-camera-d435.html 所以准备拿 ...

  3. npp基本设置

    经过实践,本人发现Notpad++是一个很不错的软件,无论是用于文档的读取还是开发,都很赞,那么给软件做一些基本的设置,使用的时候更得心用手就显得尤为重要了. 本文主要介绍npp的基础设置,后期会不断 ...

  4. PHP自定义生成二维码跳转地址

      比较简单的一款PHP自定义生成二维码跳转地址,手机端微信扫码,自动跳转到定义好的链接.支持自定义生成二维码尺寸.间距等.    鼠标悬浮显示二维码弹出层,离开后消失.js实现,代码如下: $(fu ...

  5. python-map, reduce, filter, lambda

    目录 lambda表达式 reduce()函数 map()函数 filter()函数 tips:以下使用到的迭代器,可迭代对象,生成器等概念可以参见我的另一篇博客 lambda表达式 主要用于一行写完 ...

  6. ES6 之 解构赋值

    本博文配合 阮一峰 <ES6 标准入门(第3版)>一书进行简要概述 ES6 中变量的解构赋值. 数组的解构赋值 基本用法 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这 ...

  7. 亮眼的购物季数据,高涨的 Amazon Prime

    依照往年的惯例,亚马逊公布了 2013 购物季的销售数据.据 The Verge 的报道,今年,仅仅网购星期一(Cyber Monday)一天就在全球范围内销售出 3680 万件商品,而去年这一数字为 ...

  8. 【转载】windows安装python2.7后的注册表问题

    原文出自:https://www.cnblogs.com/tlz888/p/6879227.html [提要]win平台上,python2.7官网的安装包在安装后不会添加环境变量且不会把安装信息写入注 ...

  9. loadrunner socket协议问题归纳(1)

    前段时间测了loadrunner直接发送报文到socket上的性能测试.在此,稍微回顾整理下. 与socket通讯,有两种方式,一种是建立长连接,建立后,不停的发送,接收.另外一种是建立短连接,建立连 ...

  10. php的大小写敏感问题整理

    php的大小写敏感问题整理 今天在开发php的过程中,因为命名大小写的问题导致代码错误,所以从网上整理了php的大小写敏感的一些资料,需要的朋友可以参考下.   PHP对大小写敏感问题的处理比较乱,写 ...