C++11中auto的推导规则

auto背景

在 C++11 之前的版本(C++98 和 C++ 03)中,定义变量或者声明变量之前都必须指明它的类型,比如 int、char 等;但是在一些比较灵活的语言中,比如 C#、JavaScript、PHP、Python 等,程序员在定义变量时可以不指明具体的类型,而是让编译器(或者解释器)自己去推导,这就让代码的编写更加方便。

C++11 为了顺应这种趋势也开始支持自动类型推导了!C++11 使用 auto 关键字来支持自动类型推导。
auto可以让编译器在编译期间就推导出变量的类型。

auto关键字语法糖

auto name = value

name 是变量的名字,value 是变量的初始值。

注意:auto 仅仅是一个占位符,在编译器期间它会被真正的类型所替代。或者说,C++ 中的变量必须是有明确类型的,只是这个类型是由编译器自己推导出来的。

在这里插入图片描述

auto a = 2; // 10是一个整数,默认是int型,推导结果为:int n = 10;
auto b = 3.14; // 3.14是一个小数,默认是double型,推导结果为:double b = 3.14;
auto c = &a; // &a是一个 int* 类型的指针,推导结果为:int* c = &a;
auto myName = "jiuguandianxiaoer"; // 双引号括起来的是的字符串是 const char* 类型,推导结果为:const char* myName = "jiuguandianxiaoer";

再来个栗子:

int m = 10;
auto *p = &m, n = 20; // -> int *p = &m, n = 20;

先看前面的第一个子表达式,&m 的类型是 int*,编译器会根据 auto *p 推导出 auto 为 int。后面的 n 变量自然也为 int 类型,所以把 20 赋值给它也是正确的。

这里我们要注意,推导的时候不能有二义性。在本例中,编译器根据第一个子表达式已经推导出 auto 为 int 类型,那么后面的 n 也只能是 int 类型,如果写作 n = 3.14就是错误的,因为 3.14 是double 类型,这和 int 是冲突的。

还有一个值得注意的地方是:使用 auto 类型推导的变量必须马上初始化,这个很容易理解,因为 auto 在 C++11 中只是“占位符”,并非如 int 一样的真正的类型声明。

auto的进阶用法

auto 除了可以独立使用,还可以和某些具体类型混合使用,这样 auto 表示的就是“半个”类型,而不是完整的类型。请看下面的代码:

int  x = 0;
auto *p1 = &x;   //p1 为 int *,auto 推导为 int
auto  p2 = &x;   //p2 为 int*,auto 推导为 int*
auto &r1  = x;   //r1 为 int&,auto 推导为 int
auto r2 = r1;    //r2 为  int,auto 推导为 int
  • 第 2 行代码中,p1 为 int* 类型,也即 auto * 为 int *,所以 auto 被推导成了 int 类型。
  • 第 3 行代码中,auto 被推导为 int* 类型,前边的例子也已经演示过了。
  • 第 4 行代码中,r1 为 int & 类型,auto 被推导为 int 类型。
  • 第 5 行代码是需要重点说明的,r1 本来是 int& 类型,但是 auto 却被推导为 int 类型,这表明当等号(=) 右边的表达式是一个引用类型时,auto 会把引用抛弃,直接推导出它的原始类型。

接下来,我们再来看一下 auto 和 const 的结合:

int x = 0;
const auto n = x;  //n 为 const int ,auto 被推导为 int
auto f = n;      //f 为 const int,auto 被推导为 int(const 属性被抛弃)
const auto &r1 = x;  //r1 为 const int& 类型,auto 被推导为 int
auto &r2 = r1;  //r1 为 const int& 类型,auto 被推导为 const int 类型
  • 第 2 行代码中,n 为 const int,auto 被推导为 int。
  • 第 3 行代码中,n 为 const int 类型,但是 auto 却被推导为 int 类型,这说明当=右边的表达式带有 const 属性时, auto 不会使用 const 属性,而是直接推导出 non-const 类型。
  • 第 4 行代码中,auto 被推导为 int 类型,这个很容易理解,不再赘述。
  • 第 5 行代码中,r1 是 const int & 类型,auto 也被推导为 const int 类型,这说明当 const 和引用结合时,auto 的推导将保留表达式的 const 类型。

最后我们来简单总结一下 auto 与 const 结合的用法:

  • 当类型不为引用时,auto 的推导结果将不保留表达式的 const 属性(第三行);
  • 当类型为引用时,auto 的推导结果将保留表达式的 const 属性(第五行)。

auto的限制

1)使用 auto 的时候必须对变量进行初始化。
2) auto 不能在函数的参数中使用。
这个应该很容易理解,我们在定义函数的时候只是对参数进行了声明,指明了参数的类型,但并没有给它赋值,只有在实际调用函数的时候才会给参数赋值;而 auto 要求必须对变量进行初始化,所以这是矛盾的。
3) auto 不能作用于类的非静态成员变量(也就是没有 static 关键字修饰的成员变量)中。
4) auto 关键字不能定义数组,但可以定义指针,比如下面的例子就是错误的:

char myName[] = "jiuguandianxiaoer";
auto  str[] = myName;  // myName为数组,所以不能使用 auto

5) auto无法推导出模板参数。

auto的应用

这里简单介绍一种用法,也是笔者常用的。

使用 auto 定义迭代器
auto 的一个典型应用场景是用来定义 stl 的迭代器。

我们在使用 stl 容器的时候,需要使用迭代器来遍历容器里面的元素;不同容器的迭代器有不同的类型,在定义迭代器时必须指明。而迭代器的类型有时候比较复杂,书写起来很麻烦,请看下面的例子:

#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[]){
    vector<int> test{1, 2, 3};
    vector<int>::iterator it = test.begin();
    cout << "Test is: ";
    for (; it != test.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
    return 0;
}

可以看出来,定义迭代器 i 的时候,类型书写比较冗长,容易出错。然而有了 auto 类型推导,我们大可不必这样,只写一个 auto 即可。

修改上面的代码,使之变得更加简洁:

#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[]){
    vector<int> test{1, 2, 3};
    auto it = test.begin();
    cout << "Test is: ";
    for (; it != test.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
    return 0;
}

auto 可以根据表达式 test.begin() 的类型(begin() 函数的返回值类型)来推导出变量 it 的类型。
在这里插入图片描述
世事一场大梦,人生几度秋凉?
2022.2.26

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值