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之Lanbda表达式(匿名函数)

C++11之右值引用:移动语义和完美转发(带你了解移动构造函数、纯右值、将亡值、右值引用、std::move、forward等新概念)

C++11之委派构造函数

C++11之内联名字空间(inline namespace)和ADL特性(Argument-Dependent name Lookup)

C++11之模板的别名

C++11之一般化的SFINAE规则


目录


静态类型、动态类型与类型推导

在聊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可引用于其他场合

  1. 可用于初始化列表
  2. 可用于new关键字在堆区创建变量
#include <iostream>

int main()

	auto b 1 ; // 可用于初始化列表
	auto c = new auto(1); // 可用于new关键字在堆区创建变量
	return 0;

auto不能使用的场合

  1. 不能用于函数参数
  2. 不能用于非静态成员变量
  3. 不能用于定义数组
  4. 不能当作模板参数
#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类型推导完全攻略

C++11新特性:3—— C++ decltype类型推导完全攻略

C11简洁之道:类型推导

C++11新特性之autodecltype

C++11新特性之autodecltype

深入了解C++:auto与函数模板之推导规则辨析