理解Auto的类型推导
本文最后更新于:April 20, 2022 am
在上一节讨论模板的类型参数推导的基础上,本博客将会进一步讨论建立在此机制上auto
关键字涉及的类型推导
当理解了上一节讨论的机制之后,实际上我们已经了解了auto
关键字绝大多数的奥秘,回忆函数模板的一般使用过程:
1 |
|
当编译器遇到对f的调用时,它使用expr
的类型来推导ParamType
和T
使用auto
时,auto
关键字实际上扮演的上述函数中类型参数T
的作用,连同其它的修饰符一起组成ParamType
1 |
|
不仅如此,上一节还讨论了3种情况和数组/函数的退化,auto
关键字对此也十分受用
1 |
|
auto
和模板类型参数的推导过程,只有唯一的一点不同
区别
我们从这一段代码开始观察,如果需要声明一个初始值为27的int
类型变量,C++98中,开发者有两种选择:
1 |
|
C++11以后,加入了统一形式的初始化物支持,以下两种方法也能实现
1 |
|
不论采用哪一种方法来声明变量,我们得到的结果都是一致的:一个int
类型变量,初始值是27
正如Item5中所讲的,使用auto
来替代固定的变量类型有好处,如果将int
做字面意义上的替换得到:
1 |
|
替换后的代码可以编译,但是它们的行为和替换之前就不一样了:前两行仍然会声明int
类型变量,随后赋予初始值27
问题是后两行,使用auto
关键字后,转而声明了std::initializer_list<int>
类型的变量,而非简单的int
,编译后第三行的类型最终是std::initializer_list<int>
,第四行也是如此。
这里的区别便是auto
和模板参数退推导过程不一致之处:当使用auto
关键字声明的变量的初始值是大括号形式时,auto
推导出的类型是std::initializer_list
,如果无法推导出std::initializer_list
的形式,编译器会报错:
1 |
|
此处其实有两个类型推导的过程,首先变量x5
用auto
关键字声明,我们需要使用右侧的表达式来为其推导类型,右侧的表达式的类型也需要推导,而由于无法推导出std::initializer_list<T>
中T
的类型,导致报错
上文提到大括号初始化物是auto
和模板类型推导唯一的区别,那如果我们向函数模板中传入大括号初始化物,会发生什么呢?
1 |
|
区别就在此处,使用auto
声明变量时,总是会假设大括号初始化物是std::initializer_list
,而函数模板不会。不过至于为什么如此设计的原因,就需要我们自己去搜索引擎上寻找了。但是规则就是这样,我们需要牢记,当使用auto
声明的变量的初始值是一个大括号初始化物时,变量的类型总是会被推导为std::initializer_list
对于C++11,故事到此就结束了,但是在C++14中,允许用auto
来声明函数的返回值需要被推导,此外,lambda也可能使用auto
来声明其参数的类型。好消息是,这两个新的特性使用的规则仍然是模板类型推导规则,而非auto
类型推导——也就是说,当一个使用auto
声明返回值的函数返回大括号初始化列表时,编译器会拒绝
1 |
|
对于lambda的函数参数也是一样
1 |
|
总结
auto类型推导总是和模板类型推导采用相同的规则,而区别在于auto类型推导假设大括号初始化列表代表std::initializer_list,而模板类型推导没有这种假设
函数返回值是auto,或者lambda的参数类型是auto时,使用模板类型推导规则,而非auto类型推导规则
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!