C++11之auto类型推导
Posted 林夕07
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++11之auto类型推导相关的知识,希望对你有一定的参考价值。
系列文章
C++11之正则表达式(regex_match、regex_search、regex_replace)
C++11之线程库(Thread、Mutex、atomic、lock_guard、同步)
C++11之智能指针(unique_ptr、shared_ptr、weak_ptr、auto_ptr)浅谈内存管理
C++11之强制类型转换(static_cast,const_cast,dynamic_cast,reinterpret_cast)
C++11之右值引用:移动语义和完美转发(带你了解移动构造函数、纯右值、将亡值、右值引用、std::move、forward等新概念)
C++11之内联名字空间(inline namespace)和ADL特性(Argument-Dependent name Lookup)
目录
静态类型、动态类型与类型推导
在聊auto
类型推导前,我们先理解一下静态类型和动态类型吧。
我想对于C/C++
程序员而言,静态类型应该都不陌生,因为C/C++
语言所有的变量必须遵循先定义后使用。
而python语言就是典型的动态类型,先用即用。
例如下面代码:
a = 22
print("a:", a)
对于编译器/解释器来讲,静态类型是在编译时期进行类型检查,动态类型则是在运行时期对类型检查。
运行时期对类型检查这种功能我们就称之为类型推导。
auto的历史变更
在早期的C++
中,auto
的功能是具有自动存储期的局部变量,但是因为声明变量时默认都是自动存储期的局部变量,所以auto就显得很鸡肋,为此在C++11
标准委员会就将auto
重新定义了。由一个存储类型指示符(storage-class-specifier,如 static、extern等)变成了新的类型指示符(type-specifier,如int、float等)。
注意:auto
声明的变量类型必须由编译器在编译时期推导得出,这会导致通过auto定义的变量必须赋予初值。见下图
auto类型推导的用法还是蛮简单的,这里我写了一个简单的例子。
#include <iostream>
int main()
auto a = 111;
auto b = 1.f;
auto c = a + b;
return 0;
auto的优势
auto这个关键字在实际开发中使用还是蛮频繁的(指C++11的auto)。所以对于一个经常使用STL的程序员简直就是福音,因为这能极大的改善声明变量类型的长度,使之简单化。
auto-自动类型推导
例如下面这个,对于一个map进行迭代器遍历,我们定义迭代器变量的类型就需要是std::map<int, std::string>::iterator
,这绝对是所有程序员不想看见的,太冗余了。
#include <iostream>
#include <map>
#include <iterator>
#include <string>
int main()
std::map<int, std::string> mm;
mm.insert(std::make_pair(1, "张三"));
mm.insert(std::make_pair(2, "李四"));
for (std::map<int, std::string>::iterator iter = mm.begin(); iter != mm.end(); ++iter)
std::cout << iter->first << "\\t" << iter->second << std::endl;
return 0;
那么我们就可以用auto来优化。 这样的代码又简洁又易读。
#include <iostream>
#include <map>
#include <iterator>
#include <string>
int main()
std::map<int, std::string> mm;
mm.insert(std::make_pair(1, "张三"));
mm.insert(std::make_pair(2, "李四"));
for (auto iter = mm.begin(); iter != mm.end(); ++iter)
std::cout << iter->first << "\\t" << iter->second << std::endl;
return 0;
auto还可以用在类型计算上,编译器会为你计算出最为适合的类型
#include <iostream>
int main()
int a = 3;
auto b = a * 1.1f;
auto c = a * 1.1;
return 0;
但auto
也不是万能的,在对于极端的精度计算时,auto
也无能为力。
#include <iostream>
int main()
unsigned int a = UINT_MAX;
unsigned int b = 1;
auto c = a + b;
std::cout << c << std::endl; // 0
return 0;
虽然a+b
会导致数值溢出问题,但是a+b
的返回值是unsigned int
所以auto
并不能帮你自动扩展。
auto最大的优点在于”自适应“可应用于模板中
在下面这个例子中,Sum
函数接受类型T1、T2的俩个变量,模板实例化时才可以确定类型。所以在Sum方法中的s变量的类型随着我们对模板的实例化不同它的类型也会不同。
#include <iostream>
template<typename T1, typename T2>
double Sum(T1 t1, T2 t2)
auto s = t1 + t2;
return s;
int main()
auto a = Sum<int, double>(1, 1.2);
auto b = Sum<int, int>(2, 6);
return 0;
模板实例化为Sum<int, double>
时,s的变量类型为double
。
模板实例化为Sum<int, int>
时,s的变量类型为int
。
auto在其他场景的使用与限制
auto可引用于其他场合
- 可用于初始化列表
- 可用于new关键字在堆区创建变量
#include <iostream>
int main()
auto b 1 ; // 可用于初始化列表
auto c = new auto(1); // 可用于new关键字在堆区创建变量
return 0;
auto不能使用的场合
- 不能用于函数参数
- 不能用于非静态成员变量
- 不能用于定义数组
- 不能当作模板参数
#include <vector>
void Fun(auto x = 1) // 1:auto函数参数 无法通过编译
class Test
public:
auto var = 10; // 2:auto非静态成员变量 无法通过编译
;
int main()
auto arr[3] = 1,2,3 ; // 3:auto数组 无法通过编译
std::vector<auto> arr2; // 4:auto模板参数 无法通过编译
return 0;
- 对于函数
fun
来说,auto
不能是其形参类型。你可能感觉对于fun
来说,由于其有默认参数,所以应该推导fun
形参x
的类型为int
型。但事实却无法符合大家的想象。因为auto
是不能做形参的类型的。如果程序员需要泛型的参数,还是需要求助于模板。 - 对于结构体/类来说,非静态成员变量的类型不能是
auto
的。同样的,由于var
定义了初始值,你可能认为auto
可以推导str
成员var
的类型为int
的。但编译器阻止auto
对结构体/类中的非静态成员进行推导,即使成员拥有初始值。 - 声明
auto
数组。我们可以看到,main
中的声明auto arr[3]
这样的数组同样会被编译器禁止。 - 在实例化模板的时候使用
auto
作为模板参数,如main中我们声明的vector<auto> v
。你可能认为这里一眼而知是int
类型,但编译器却阻止了编译。
以上4种情况的特点基本相似,我们其实可以很容易能够推导出auto
所在位置应有的类型,但现有的C++11的标准还没有支持这样的使用方式。
要注意C++98的auto与C++11的auto不要混用,因为在俩个版本的auto功能是完全不同的。
以上是关于C++11之auto类型推导的主要内容,如果未能解决你的问题,请参考以下文章
C++11新特性:3—— C++ decltype类型推导完全攻略