C++11特性的学习之新手易学,老兵易用(三)

  目录括号内为适合人群,所有库作者的内容暂不做学习,可自行查阅《深入理解C++11:C++11新特性解析与应用》。网盘链接: https://pan.baidu.com/s/1Jf29R7-foOoXJ5UW3mTKVA 密码: 7vgq

目录

1.右尖括号>的改进(所有人)
2.auto类型推导(所有人)
  ①静态类型,动态类型与类型推导
  ②auto的优势
  ③auto的使用细则
3.decltype(库作者)
  ①typeid与decltype
  ②decltype的应用
  ③decltype推导四规则
  ④cv限制符的继承与冗余的符号
4.追踪返回类型(库作者)
  ①追踪返回类型的引入
  ②使用追踪返回类型的函数
5.基于范围的for循环(所有人)

1.右尖括号>的改进 ^

  在C++98中,如果在实例化模板的时候出现了连续的两个右尖括号>,那么它们之间需要一个空格来进行分隔,如下:

vector<vector<int>> ivec;//在C++98标准中,会出现错误
vector<vector<int> > ivec;//在C++98标准中的写法

  在C++11标准中,要求编译器智能地去判断在哪些情况下>>不是右移符号。不过这会带来一些与C++98的不兼容性,如下:

template <int i> class X{};
//这里的>>是右移操作符
X<1 >> 5> a; //在C++98中编译通过,而C++11中将报错。这是因为C++11中编译器优先将尖括号进行配对。

X<(1 >> 5)> a;//加上括号,则可避免这种情况

2.auto类型推导 ^

  

  ①静态类型,动态类型与类型推导 ^

  静态类型:每个变量使用之前必须声明定义。而动态类型,如Python,Perl,JavaScript等语言中变量不需要声明,几乎是“拿来就用”的变量使用方式。不过从技术上严格来说,静态类型和动态类型的主要区别在于对变量进行类型检查的时间点。对于静态类型,类型检查主要发生在编译阶段;对于动态类型,类型检查主要发生在运行阶段,这归功于类型推导。

  在C++11中类型推导的实现方式之一就是重定义了auto关键字,另一个实现是decltype。

int main()
{
    auto name="hello world";
    cout<<name<<endl;     //这里name被推导为const char*类型
}

auto关键字声明的变量必须被初始化,否则会报错

  ②auto的优势 ^

  auto推导的一个最大优势就是在拥有初始化表达式的复杂类型变量声明时简化代码。如下:

int main()
{
    vector<int> ivec{ 1,2,3,4,5,6,7,8,9 };
    auto iter = ivec.begin();//不使用auto时的语句:vector<int>::iterator iter = ivec.begin();
    while (iter != ivec.end())
        cout << *iter++ << " ";//遍历输出容器内的元素
    cout << endl;
}

  auto的第二个优势则在于免除我们在一些类型声明时的麻烦,或者避免一些在类型声明时的错误。但对于数值溢出的问题,auto也无能为力,如:

int main()
{
    unsigned int a = 4294967295;
    unsigned int b = 1;
    auto c = a + b;
    cout << "a:" << a << endl;        //输出:4294967295
    cout << "b:" << b << endl;        //输出:1
    cout << "a+b:" << c << endl;      //输出:0
}

  auto的第三个优势就是其“自适应”性能够在一定程序上支持泛型的编程。即auto a=5;的等号右边,如果改变了类型,auto修饰的a也能适应新的类型。对于在不同的平台上的代码维护,auto也会带来一些好处。当应用在模板中时,其“自适应”性会得到更加充分的体现,如下:

扫描二维码关注公众号,回复: 3109964 查看本文章
#include <iostream>
#include <string>
using namespace std;

template<class T1,class T2>
auto add(const T1& t1,const T2& t2)
{
    return t1 + t2;
}
int main()
{
    string str = "hello ";
    cout << add(5, 2.9) << endl;    //输出:7.9
    cout << add(str,"world") << endl; //输出:hello world
}

  ③auto的使用细则 ^

  ①在C++11中,auto可以与指针和引用结合起来使用,值得注意的点是如果要用auto声明的变量是另一个变量的引用,则必须使用auto&。指针的话则会自动推导。

  ②C++11标准规定auto可以与cv限制符(即常量的const和易变的volatile)一起使用,不过声明为auto的变量并不能从其初始化表达式中“带走”cv限制符。

int main()
{
    const int a = 5;
    auto b = a;      //b的类型为int,并不是const int
}

  ③跟其他变量指示符一样,同一个赋值语句中,auto可以用来声明多个变量的类型,不过这些变量的类型必须相同,如果不同则报错。可以选择每一个auto变量的声明写成一行。

  只要能够进行推导的地方,C++11都为auto指定了详细的规则,保证编译器能够正确地推导出变量的类型。包括C++11新引入的初始化列表,以及new,都可以使用auto关键字。

  不过auto也不是万能的,受制于语法的二义性,或者是实现的困难性,auto往往也会有使用上的限制,如下:

void func(auto i) {}  //①函数的参数列表不能使用auto,无法通过编译

class A
{
    auto a = 10;    //②非静态成员变量不能使用auto,无法通过编译
};

auto a[3] = { 1,2,3 }; //③auto数组不能使用,无法通过编译

vector<auto> vec;    //④模板参数不能使用auto,无法通过编译

3.decltype ^

  暂不作学习,可查阅《深入理解C++11:C++11新特性解析与应用》。

  ①typeid与decltype ^

  

  ②decltype的应用 ^

  

  ③decltype推导四规则 ^

  

  ④cv限制符的继承与冗余的符号 ^

  

4.追踪返回类型 ^

  暂不作学习,可查阅《深入理解C++11:C++11新特性解析与应用》。

  ①追踪返回类型的引入 ^

  

  ②使用追踪返回类型的函数 ^

  

5.基于范围的for循环 ^

  在C++98标准中,我们遍历数组或容器用到的方法如下所示:

int main()
{
    vector<int> ivec{ 1,2,3,4,5,6,7,8,9,10 };
    auto iter = ivec.begin();
    while (iter != ivec.end())  //利用迭代器遍历容器
        cout << *iter++ << " ";
    cout << endl;
}

  在C++11标准中,引入了基于范围的for循环,如下:

int main()
{
    vector<int> ivec{ 1,2,3,4,5,6,7,8,9,10 };
    for(auto i:ivec)        //auto是该容器内元素的类型,i为变量,ivec为该容器名
        cout << i<< " ";
    cout << endl;
}

  对比可知,C++98标准中遍历数组或容器,我们时常要担心取值范围是否合理,一不小心就会越界访问。而在C++11标准中的基于范围的for循环则无需担心这些。基于范围的for循环跟普通循环是一样的,可以用continue语句来跳过循环的本次迭代,而用break语句来跳出整个循环。

  不过是否能够使用基于范围的for循环,必须依赖一些条件。如下:

  • for循环迭代的范围是可确定的
  • 基于范围的for循环要求迭代的对象实现++和==等操作符
  • 注:基于范围的for循环使用在标准库的容器中时,如果使用auto来声明迭代的对象的话,那么这个对象不会是迭代器对象。

    猜你喜欢

    转载自blog.csdn.net/qq_17044529/article/details/82468332
    今日推荐