引言

auto : 类型推导. 在使用c++的时候会经常使用, 就像在考虑STL时迭代器类型, 写模板的时候使用auto能少写代码, 也能帮助我们避免一些隐患的细节.

auto初始化

  1. 使用auto型别推导要求必须在定义时初始化, 毕竟需要根据对象的类型推导左值对象的型别.
auto j; 	// error. 必须初始化
auto i = 0; // i 推导型别为 int
vector<int> v;
auto vv = v.cbegin(); // vv 推导型别为 const int*
  1. 但是auto型别推导会忽略引用和顶层const, 所以要对对象加上想要的修饰.
const int ci = 0;
auto i = ci; // i 推导型别为 int, 忽略了顶层const
int &ri = i;
auto ii = ri; //ii 推导型别为 int, 忽略了引用
  1. C11之前只能通过()=对变量初始化, C++11增加了对定义的对象初始化的方法,可以使用{}对变量初始化

c11之前的初始化方法

int i(0);	// i 初始化 0
int j = 0; // j 初始化 0

c11后的初始化方法

auto i(0); auto j = i;	// 支持c11前
auto ii{0}; // 使用 {} 进行初始化, 但是auto推导只能接受一个参数
auto jj = { 0 }; // jj 的推导型别为 initializer_list<int>型别

上面jj的推导居然不是int型别, 而是 initializer_list<int> , 这不能怪auto推导出问题, 这主要是后者的对象初始化就是使用={}, 可以说是auto推导的是最精确的型别. 不管新添的初始化方法, 找一个习惯的就行了.

auto与for

auto最常见的就是与for联用, 特别是类型特别复杂的时候. 但是auto又有多种选择, 如 : auto, auto &等, 不同的选择其效率也不一样.

  1. auto , 即 for(auto i:range) . 这使range中的每一个元素都会产生一个副本, 所以即使修改了 i 也不会实际影响到range.
  2. const auto, 及for(const auto i : range). 这也会是range的每一个元素产生一个副本, 但是这个副本竟不能被修改.
  3. auto &, 即for(auto &i : range). 引用, 因为i 直接引用range里面的元素, 所以并不会产生一个副本, 但是 i 的修改也会影响range里元素的值. 通常我们需要修改range是会考虑用到.
  4. const auto&, 即for(const auto &&i : range). i 直接引用range里面的元素, 所以并不会产生一个副本, 并且i 也不能修改. 一般初始化的是一个左值时而且是读取range里的元素时都是用const auto&而不用auto, 因为前者不会产生副本, 效率要高. 当然一般初始化的是一个左值时效率低, 但是如果是右值还是使用const auto效率高, 因为const auto &需要把 i 存储在内存中的一个位置,间接访问会更消耗时间
  5. auto&&, 即for(auto &&i : range). 如果初始化是左值, 那么 i 就是左值引用, 如果初始化是右值, 那么 i 就是右值引用,

还有const auto &, 当然具体的选择还是看具体的情况而定.

最后, 当用auto推导多维数组的时, 保证除最内层循环外, 其他的外层循环都应该是引用类型, 否则很容易出错, 即 :

int a[10][10][10];
for (const auto &i : a)
for(const auto &j : i)
for(const auto k : j)
;

最好使用auto型别推导

1. 初始化

  • 在定义对象的时候可能或多或少会忘记对变量进行初始化, 但当我们使用该变量的时候就会出错, 而且问题也不好找出来, 但是使用auto定义对象就要求必须初始化有时还能减少代码量, 上面我们已经分析过了.
  • 使用auto初始化在平台上还有一点好处, 比如 :
vector<int> v;
unsigned size = v.size(); // size()返回size_t型别
auto sizet = v.size();

​ 在32的平台unsigned代表的是32位, size_t是32位, 在64的平台unsigned代表的也是23位, 但是size_t却是64位, 这样平台差异可能就会带来问题, 使用auto代替就没有这样的问题.

不过只有这几点可能不会让人心动, 下面我们还有auto的好处.

2. STL使用型别推导

还记得在前言中个说过调用STL最好使用auto推导型别, 如果你还记得mappair 吗? 是这样 map<pair<key, type>>? 还是map<pair<const key, type>>? 答案是最后一种, 那么现在我们就来分析的使用auto推导还是显示型别比较好.

int main()
{
std::map<string, std::function<type(type, type)>>func = {
{ "+", [](auto i, auto j)->auto {return i + j; } },
{ "-", [](auto i, auto j)->auto {return i - j; } },
{ "*", [](auto i, auto j)->auto {return i * j; } },
{ "/", [](auto i, auto j)->auto {return i / j; } }
}; for (const auto &i : func) ; for(const std::pair<string, std::function<type(type, type)>> &pa : func) ; system("pause");
exit(EXIT_SUCCESS);
}

看到上面的例子毫无问题, 但是深究起来显示型别还是些不完美. 我们知道map的key不能被改变, 所以显示型别的string与map的const string不是匹配, 编译器就会将map对象都会产生一个临时对象再隐式的转为string, 等等. 是不是注意到有一点了, 为了型别匹配赋值会产生临时变量, 那岂不是每一循环都会产生一个临时变量, 但是auto型别推导就是精确匹配的, 不会产生临时变量.

可能觉得将显示型别的key改为const string就能解决这个问题了, 确实是这样, 但是如果没有注意到这一点细节, 那就会损失效率了, 使用auto可以完全不想这些问题啊.

当然使用显示型别还是型推导看实际也看个人, 不是必要.

auto与函数返回类型

auto不能被声明为返回值,auto不能作为形参,auto不能被修饰为模板参数. 那么这里auto还能怎么和函数关联起来? 能.

auto放在函数名前面告诉编译器,真正的返回值在函数声明之后. 简单说auto可以作为返回值占位符来使返回值后置.

就像这样来写.

auto Return(std::size_t N) -> std::size_t
{
return N;
}

既然c++规定可以这样写肯定有其意义. 其实这个写法主要用于template中, 当返回值的类型是一个模板类型时使用, 而返回值类型通过decltype来推导.

这里就解释一下decltype的简单运用. , decltype也是类似与auto的关键字, 都能够进行参数类型推导, 但是decltype必须要接受一个参数, 如下:

int i = 1;
decltype(i) j = 1;

auto与模板函数连用时用模板参数作为返回值. 因为编译器并不能直接推断出返回值为类型参数的实际类型, 所以在STL中采用traits编程解决这个问题, 这里时另一种实现方法.

首先看一个错误的例子:

template<class T1, class T2, class T3>
T3 fun(T1 t1, T2 t2) {...}

T3的类型要在函数返回的时候才能知道, 而函数这样写就必须要编译期间就要知道返回值类型. 所以编译器会报错.

以下这样写就是正确的, 但是必须保证编译器能推导出类型.

template<class T1, class T2, class T3>
T1 fun(T1 t1, T3 t3) {...}

使用auto将返回值类型放在最后, 就是告诉编译器真正的返回值在编译后动态获取, 而auto在这里的作用也称为返回值占位

template<class T1, class T2>
auto fun(T1 t1, T2, t2) -> decltype(*t1) {...}

以上可以将返回类型放在函数尾做尾置是C11中的要求, 但是C14已经可以将返回型别放推导在函数头. 如 :

template<class T1>
decltype(auto)fun() {...} // 这样的写法同上式一样

虽然规定能够这样写, 有时为了兼容也还是写成尾置.

auto与new运算符

我们可以使用auto来推断出new对象的类型, 但是局限在于, 必须对new出来的对象进行单一的初始化.

auto i = new int; // 这中写法根本没有用到auto的推导哦, 因为new的类型已经确定了

auto i = new auto(1);		// 这里就是用到了auto推导
auto size = new auto; // error, 不能推导出size的类型
auto j = new auto(1,2); // error, 只能接收一个初始化值

const中我们分析到顶层const会被忽略, 所以auto是无法推断出顶层const, 即 :

auto i = new const auto(1); 	// 这里auto并没有推导出顶层const, 所以i的类型实际上是int
const auto j = new const auto(1); // 只有显示的定义j的类型是const

如果想直接推导出顶层const的话, 最好还是decltype进行推导.

注意 : auto推导只能推导出int, double等, 不能推导出short类型.

总结

本节对C11的auto用法做了一个浅显的分析, 分别对使用auto的好处, 定义时注意{}对象也必须初始化, auto在与for连用的时候要根据实际参数确定选择哪种实现, 这样效率才会达到最大, 当然一般都使用const auto&auto&&. 最后还对auto与函数返回值关联, 可以将返回型别放在函数名尾也可以, 这样的做法一般在模板中将模板参数作为返回值才考虑用, 平时也不必这样定义函数.

参考 :

<< Effective Modern C++ >>

auto, auto&, const auto&以及其它形式的auto变种在for-range loop的选择

auto类型推导的更多相关文章

  1. Effective Modern C++翻译(3)-条款2:明白auto类型推导

    条款2 明白auto类型推导 如果你已经读完了条款1中有关模板类型推导的内容,那么你几乎已经知道了所有关于auto类型推导的事情,因为除了一个古怪的例外,auto的类型推导规则和模板的类型推导规则是一 ...

  2. 第3课 auto类型推导(2)

    第3课 auto类型推导(2) 一.使用auto的优势 (一)避免使用未初始化变量 (二)可简化变量/对象类型的声明 (三) 在某些场合无法判断出类型时,可用auto自动推导(如lambda表达式) ...

  3. 第2课 auto类型推导(1)

    第2课 auto类型推导(1) 一.auto类型推导 (一)与模板类型推导映射关系 1.auto类型推导与模板类型推导可以建立一一映射关系,它们之间存在双向的算法变换.auto扮演模板中T的角色,而变 ...

  4. 类型推导:函数模板与auto

    1.从函数模板谈起 函数模板的类型推导机制是在c++98时代就有的,auto的类型推导机制与其基本一致,所以先理解函数模板类型推导. 函数模板可以用如下代码框架表示: #template<typ ...

  5. 《Effective Modern C++》翻译--条款2: 理解auto自己主动类型推导

    条款2: 理解auto自己主动类型推导 假设你已经读过条款1关于模板类型推导的内容,那么你差点儿已经知道了关于auto类型推导的所有. 至于为什么auto类型推导就是模板类型推导仅仅有一个地方感到好奇 ...

  6. item 2: 理解auto类型的推导

    本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 如果你已经读过item 1的模板类型推导,你已经知道大部分关于au ...

  7. c++11——auto,decltype类型推导

    c++11中引入了auto和decltype关键字实现类型推导,通过这两个关键字不仅能够方便的获取复杂的类型,而且还能简化书写,提高编码效率.     auto和decltype的类型推导都是编译器在 ...

  8. 模板类型推导、auto推导

    effective modern c++ 果然是神书,干货满满,简单记录下. item1 模板推倒 典型的模板函数 temlate<class T> void fn(ParamType p ...

  9. C++11 类型推导decltype

    我们之前使用的typeid运算符来查询一个变量的类型,这种类型查询在运行时进行.RTTI机制为每一个类型产生一个type_info类型的数据,而typeid查询返回的变量相应type_info数据,通 ...

随机推荐

  1. unity3d-23种设计模式全解析

    http://www.jianshu.com/nb/4161593 2016.08.03 09:26 字数 1203 阅读 584评论 0喜欢 14 希望大家能共同学习,交流 谢谢支持zero(QQ: ...

  2. 【bzoj1028】[JSOI2007]麻将

    首先枚举等待牌,再枚举对子牌.   然后1~n扫一遍,如果现在 s[i]不能被3整除,那么必须跟后两个数搭配几下变成能被3整除的.然后如果能被3整除,那么只要三个连续的一组可行,则三个相同的一组必定也 ...

  3. whl 安装

    pymssql 安装 C:\Users\sas>pip install d:\pymssql--cp36-cp36m-win_amd64.whl Processing d:\pymssql--c ...

  4. linux替换目录下所有文件中的某字符串

    linux替换目录下所有文件中的某字符串 比如,要将目录/modules下面所有文件中的zhangsan都修改成lisi,这样做: sed -i "s/zhangsan/lisi/g&quo ...

  5. js将图片转为base64编码,以字符串传到后台存入数据库

    (前台在中approve_edit.html中,后台不变) 链接参考:http://www.cnblogs.com/Strom-HYL/p/6782176.html 该链接文中并没有用到easyUI的 ...

  6. HDU 5862Counting Intersections

    Counting Intersections Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/ ...

  7. php获得两个字符串公共最大子串的函数

    <?php header("Content-type: text/html; charset=utf-8"); function search($str1,$str2) { ...

  8. [App Store Connect帮助]三、管理 App 和版本(2.4)输入 App 信息:提供加密出口合规证明文稿

    上传至 App Store Connect 的 App 被上传至位于美国的 Apple 服务器.如果您提交 App 的目的是为了在 App Store 上分发您的 App 或通过美国或加拿大的境外 T ...

  9. http通讯基础

    1 . 一个网页包括 JS  CSS Html 2 . 状态码:200  正常   302  临时重定向 (类似呼叫转移) 304  未修改,客户端缓存的信息是最新的,无需到服务器重新获取 403  ...

  10. akka设计模式系列-Chain模式

    链式调用在很多框架和系统中经常存在,算不得上是我自己总结的设计模式,此处只是简单介绍在Akka中的两种实现方式.我在这边博客中简化了链式调用的场景,简化后也更符合Akka的设计哲学. trait Ch ...