文档开发者中心
我的

C++11之为何你要尽量使用auto?


对于 C++11 的 auto 关键字,感觉使用的过程并没有做到得心应手,知其然不知其所以然。


所以系统学习并总结一下,我们什么时候应该使用 auto 来定义变量。


1)当然是少写 N 多的字符


// 获取迭代器的类型
typename std::iterator_traits<It>::value_type currValue = *b; //C++98写法
auto currValue = *b; //C++11写法,可以看到简练很多


2)避免未初始化的变量


int x1; // 未初始化
auto x2; // 编译错误!使用auto必须初始化
auto x3 = 0; // 正确!


3)直接持有closure(闭包)


// 类比较函数(函数类型【这里应该特指返回值类型,因为参数类型是知道的】仅编译器知道)
auto derefUPLess = [](const std::unique_ptr<Widget>& p1,
                     const std::unique_ptr<Widget>& p2) {
   return *p1 < *p2;
};
// c++14 auto可以用于函数参数,写法将更加简洁
auto derefUPLess = [](const auto& p1,
                     const auto& p2) {
   return *p1 < *p2;
};


《Effective Modern C++》中有提到,auto 声明比std::function性能更好(后者会涉及到一些复制操作等,未深究)。


4)避免类型不匹配问题


例1

std::vector<int> v;
…
unsigned sz = v.size();


v.size()的返回值是std::vector<int>::size_type,从官网上的说明可以看到它就是size_t(unsigned integer types)。对于 Intel 架构平台,各数据类型实际所占字节如下(引自Integer sizes in C on 32 bit and 64 bit Linux):


type  executable[1]32 bit64 bit
short int16 bit16 bit
int32 bit32 bit
long int32 bit64 bit
long long int64 bit64 bit
size_t32 bit64 bit
void* [2]32 bit64 bit


可以看到,对于 64 位平台,[unsigned ] int与size_t不等,所以上述代码在 64 位平台下可能出现“类型截断”。这时候,使用 auto 就是更好的选择了:


auto sz = v.size(); // sz's type is std::vector<int>::size_type


例2


std::unordered_map<std::string, int> m;
…
for (const std::pair<std::string, int>& p : m)
{
… // do something with p
}


这里有一个陷阱需要注意,就是 map 的 key 是不可变的(map keys are semantically immutable),所以所有对于 map key 的直接操作(譬如解引用迭代器,产生value_type),都会对 key 加上const属性(参考What is difference between const and non const key?)。


也就是说,上面的例子中,map 中的std::pair类型是std::pair<const std::string, int>,而不是std::pair<std::string, int>。 所以将 p 声明为std::pair<std::string, int>后,编译器会尝试将std::pair<std::string, int>转换为std::pair<const std::string, int>。这个过程是这么实现的:对每个 p 想要绑定的 m 中的一员,先将其复制到一个临时对象,然后将 p 绑定到这个临时变量上。在每次循环最后,这个临时对象会被销毁。


上面的过程,其实与你的“初心”是不符的,因为你仅仅只是想将 p 绑定到 m 的所有元素而已。


当然,这时候使用 auto 就能避免这样的“类型不匹配”了:


for (const auto& p : m)
{
… // as before
}


除去类型正确、字符更少的好处,这里还有一点值得提出,就是获取 p 的地址的时候,可以正确的拿到 m 里元素的地址。在不使用 auto 的写法中,获取 p 的地址拿到的是临时对象的地址,而这个临时对象是在循环结束前会被销毁的。


5)利于代码重构


想像一个场景:你定义的一个函数返回int类型,然而随着你的软件使用,int已经不能满足你的需求,你需要将返回值改为long。此时,假如在获取返回值的地方,你是用的是 auto,那么你只需要在函数声明和函数定义处修改。假如你是用的是精确类型定义,那你就需要找出每个调用了此函数并用int类型获取返回值的地方,并全部修改它们。


总结


auto 可以显著减少输入的字符数,可以避免未初始化变量的出现(因为定义 auto 变量必须初始化),也可以防止因为类型不匹配导致的程序错误(例1)及性能问题(例2),对代码重构的工作量也有一定的帮助。


当然,也存在一些不能使用 auto 的场景,下回分解。


BY:HW-LH

点赞
收藏
回复
8
分享
举报
浏览3973 编辑于2019-10-10 08:18未知归属地
全部评论
最多点赞
最新发布
最早发布

关联阅读:Code Review 最佳实践

6楼回复于2019-10-30 09:01 未知归属地

关联阅读:C++11并发三条规则

5楼回复于2019-10-30 09:01 未知归属地

关联阅读:C++11中condition_variable的陷阱

4楼回复于2019-10-30 09:01 未知归属地

关联阅读:C++11之啥时候不要用 auto?

3楼回复于2019-10-30 09:01 未知归属地

默默的猜测后面可能是C++不要用啥

2楼回复于2019-10-11 09:43 未知归属地

auto 还是很不错的

1楼回复于2019-10-10 09:47 未知归属地
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

了解社区公约,与您携手共创和谐专业的开发者社区。