C++11: auto 关键字

前言

在 C++11 以前,auto 关键字的语义继承自 C 语言,表示 进入块后,自动分配内存,即分配堆栈内存。也就是说 auto 只能用于函数内,然而函数内部的声明变量默认使用的就是这种分配,因此在 C++11 以前,auto 关键字几乎没什么用处。
而在 C++11 以后,auto 关键字被赋予了新的语义:自动类型推导

auto 与 decltype 作用类似,但有一定的区别


1. 推导规则

语法:auto 变量名 = 变量值

auto 只是类型的 “占位符”,不是一个实际类型,它需要被编译器推导出来,因此:auto 声明的变量必须初始化,在编译时 auto 被真正的类型给替换掉。

推导规则满足:
当 表达式右部 的类型为

  • 一般类型 (非指针、非引用):auto 就是对应的类型,但 顶层 const 被忽略

    如果你对 顶层与底层 const 不了解的,可见文章:【C++ const 详解】 的第 4 部分。

    int 	  a  = 0;
    const int ca = a;
    ----------------------------------------
    auto x = 0;		// auto: int
    auto x = 1.0;	// auto: double
    auto x = 'a';	// auto: char
    auto x = a;		// auto: int
    auto x = ca;	// auto: int (顶层 const 被忽略)
    
  • 指针类型顶层 const 忽略,底层 const 不忽略保留指针类型
    int  a = 0;
    int* p = 0;
    
    const int* cp = &a;
    int* const pc = &a; 
    ---------------------------------------
    auto x = p;		// auto: int*
    auto x = cp;	// auto: const int*	(底层 const 不忽略)
    auto x = pc;	// auto: int* (顶层 const 忽略)
    
  • 引用类型:引用被忽略,const 不保留
    int   a  = 0;
    int&  lr = a;
    int&& rr = 0;
    
    const int&  clr = a;
    const int&& crr = 0; 
    -----------------------------------
    auto x = lr;	// auto: int (引用被忽略)
    auto x = rr;	// auto: int
    auto x = clr;	// auto: int (引用被忽略,const 不保留)
    auto x = crr;	// auto: int
    

volatile 关键字也满足上述规则中的 const 的规则

既然对于引用类型:引用 与 const 都被忽略,如果我们需要保留,那就需要手动指定。但是注意:auto 的推导仍然满足上面的推导过程

【例】const auto& x = a; (int&& a = 0;)
【解】auto: int;故在编译时 auto 被替换为 int (auto 只是占位符),那么上述语句相当于:const int& x = a,因此 x 的类型为 const int&。

在这里插入图片描述

【注】 引用是 右值引用 时,需要注意 引用折叠 问题,详情可见文章:【右值引用】 中的 4.1 部分。

也就是说,auto 在编译时先被编译器通过表达式右部的类型推导出来,并替换掉,而后 auto 修饰的变量的最终类型为 手动指定的类型 + auto 的实际类型。

但是 auto 并非时万能的,其中一个重要原因是:auto 是编译时推导


2. 不能使用 auto 的场景

  • 不能指定为 函数参数 的类型。

    可以这么理解:auto 是再编译时推导的,但是函数形参需要在函数被调用时,也就是运行时,才能被确定

    void fun(auto x);	// 错误
    
  • 对于类的成员,只能用于指定 非静态 const 成员 的类型。
    class A
    {
    	auto a = 0;	// 错误
    	static auto b = 0;	// 错误
    	static const auto c = 0;	// 正确 
    };
    
  • 无法定义数组
    int arr[] = { 0, 1, 2 };
    auto x   = arr;		// auto: int*
    auto x[] = arr;		// 错误
    auto x[] = { 0, 1, 2 };		// 错误
    
  • 无法利用 auto 推导模板类型
    template <typename T>
    class Test { };
    
    Test<int>  t;
    Test<auto> tt = t;	// 错误:无法推导 auto 的类型
    

那么什么时候比较合适使用 auto 呢?


3. 常见的使用场景

  • STL 容器的遍历
    • 不使用 auto 遍历,那么迭代器的类型会比较长:
    vector<int, vector<int>> t;
    vector<int, vector<int>>::iterator it = t.begin();
    for (; it != t.end(); it++) {}
    
    • 使用 auto 可以大大降低代码长度
    vector<int, vector<int>> t;
    for (auto it = t.begin(); it != t.end(); it++) {}
    
  • 模板中指定不确定函数的返回值类型
    在模板编程中,有时会出现着这种情况,在不同类中,统一函数,但是返回值不同(这种设计有点不合常理,但是可能存在)
    class A 
    {	
    	int fun();
    };
    
    class B
    {	
    	string fun();
    };
    // 两个 fun 返回值类型不同
    // 模板函数 f 中可能会调用 A、B 中的 fun 函数 
    template <typename T>
    void f(T t)
    {
    	// int x = t.fun();
    	// 还是 string x = t.fun();
    	// 如果指定为 int ;,那么当传入的 T = B 时,会报错:因为 B::fun() 返回值是 string
    	// 那么可以如下指定:
    	auto x = t.fun();
    	// ... ...
    }
    
  • … …

它的使用场景有许多,这取决于编写代码者自己本身,但是需要注意:在符合 auto 使用规则的前提下,如果程序大片的使用 auto,这可能使得代码可读性很差。


本文如有错误,欢迎批评指正。

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值