现代C++之理解auto类型推断
理解auto类型推断
上一篇帖子中讲述了模板类型推断,我们知道auto的实现原理是基于模板类型推断的,回顾一下模板类型推断:
template <typename T>
void f(ParamType param);
使用下面的函数调用:
f(expr);
我们看到模板类型推断过程涉及到了模板template、函数f以及参数(包括模板参数和函数参数),调用f的时候,编译器会推断T和ParamType的类型。auto的实现和这三个部分是有着对应关系的。当使用auto声明一个变量,auto关键字扮演的是模板类型推断中T的角色,而类型说明符扮演的是ParamType的角色。看下面的例子:
auto x = 27; //类型说明符就是auto自己
const auto cx =x; //类型说明符为const auto
const auto& rx =x;//类型说明符为const auto&
编译器使用auto对上面的类型进行推断就如同使用了下面的模板类型推断:
template<typename T>
void func_for_x(T param); //ParamType即非引用也非指针
func_for_x(27); // 推断x的类型,T为int ,ParamType 为 int
template<typename T>
void func_for_cx(const T param); //ParamType即非引用也非指针
func_for_cx(x); //用于推断cx的类型,T为int,ParamType为 const int
template<typename T>
void func_for_rx(const T& param);//ParamType为引用
func_for_rx(x); // 用于推断rx的类型,T为int,ParamType为const int&
继续回顾上一篇帖子的内容,基于ParamType的三种形式,模板类型推断也对应着三种不同情况。而auto的类型说明符扮演的是ParamType,因此使用auto进行变量声明,也会有三种情况:
- 类型说明符是指针或者引用类型,但不是universal reference
- 类型说明符是universal reference。
- 类型说明符即非指针也非引用。
上面举的例子是第一种和第三种情况:
auto x = 27; //case 3 x类型被推断为int
const auto cx = x; //case 3 cx被推断为 const int
const auto &rx = x; //case 1 rx被推断为const int &
举一个情况2的例子:
auto&& uref1 = x; //x为左值,uref1被推断为左值引用
auto&& uref2 = cx; // cx const int 左值,uref2被推断为const int &
auto&& uref3 = 27; // 27 为 int 右值,uref3被推断为 int &&
上篇帖子介绍了对于模板中的非引用ParamType,传入函数或者数组实参的时候会退化为指针的情况(而使用引用ParamType的时候,数组实参会被推断为指向数组的引用),auto类型推断也会如此:
const char name[] = "R. N. Briggs";
auto arr1 = name; // arr1 的类型为const char*
auto& arr2 = name; // arr2 的类型为const char (&)[13]
void someFunc(int, double);
auto func1 = someFunc; // func1的 类型为 void (*)(int, double)
auto& func2 = someFunc; // func2的类型为 void (&)(int, double)
上面介绍的都是auto和模板类型推断使用原理相同的部分,下面说的不一样的。
C++98中初始化一个Int有两种方式:
int x1=27;
int x1(27);
在C++11中,支持统一初始化(uniform initialization):
int x3 = {27};
int x3{27};
四种语法形式的结果只有一个,初始化一个Int值为27。这里我们将都使用auto进行初始化:
auto x1 = 27;
auto x2(27);
auto x3 = {27};
auto x4{27};
上面的四句话都能编译通过,但并没有和原来的四种形式意义完全一致。前面两个是一样的,后面两句话声明的变量类型是std::initializer_list,其中包含了单个元素,值为27。
auto x1 = 27; //x1为int,值为27
auto x2(27);//同上
auto x3 = {27};//x3为 std::initializer_list<int>,值为{27}
auto x4{27}; //同上
这里就用到了一个对于auto的特殊类型推断规则:当用大括号括起来的值对auto变量进行初始化的时候(叫做统一初始化式),变量类型会被推断为 std::initializer_list。如果不能够推断成此类型(比如,大括号中的值不是同一类型),编译会出错:
auto x5 = { 1, 2, 3.0 }; // error! 类型不一致,不能将推断为std::initializer_list<T>
这里会发生两种类型推断,一种是将统一初始化式推断为std::initializer_list ,而std::initializer_list本身也是一个类型为T的模板,因此会根据统一初始化式中的实参对T进行模板类型推断,这是第二种类型推断。上面的类型推断会失败是因为第二种类型推断会失败。
对统一初始化式的处理的不一致是auto和模板类型推断的唯一区别。使用统一初始化式对auto变量初始化会将其推断为std::initializer_list,但是模板类型推断不会这么做:
auto x = { 11, 23, 9 }; // x的类型为 std::initializer_list<int>
template<typename T> // 和auto x等同的模板类型推断
void f(T param);
f({ 11, 23, 9 }); // 错误!这里不能推断T的类型。
如果要达到auto的效果,得按照下面的方式来做:
template<typename T>
void f(std::initializer_list<T> initList);
f({ 11, 23, 9 }); // T被推断为int, initList 的类型为 std::initializer_list<int>
在C++11中使用auto时,这里比较容易出错,你本来想声明别的变量,最终却将其声明成了一个 std::initializer_list。因此,要谨慎使用统一初始化。
在C++14中,允许将auto作为函数返回值,也可以用其修饰lambda表达式中的参数。但是这些auto使用的都是模板类型推断,而不是auto类型推断,因此一个函数返回值为auto 类型时,返回统一初始化式的值会出错:
auto createInitList()
{
return { 1, 2, 3 }; // 错误!不能推断{1,2,3}
}
下面的方式是对的:
std::initializer_list<int> createInitList()
{
return { 1, 2, 3 }; //
}
最后总结一下:
- 模板类型推断是auto的基础,auto关键字扮演了模板类型推断中的T,而类型说明符扮演的是ParamType。
- 对于模板类型推断和auto类型推断,大多数场景下推断规则相通,有一种特殊情况,就是统一初始化式。
- C++14中使用auto可以作为函数返回值,也可以作为lambda表达式的参数修饰符,但需要注意,这里的auto使用的是模板类型推断,而不是auto类型推断。
现代C++之理解auto类型推断的更多相关文章
- 现代C++之理解模板类型推断(template type deduction)
理解模板类型推断(template type deduction) 我们往往不能理解一个复杂的系统是如何运作的,但是却知道这个系统能够做什么.C++的模板类型推断便是如此,把参数传递到模板函数往往能让 ...
- [Effective Modern C++] Item 2. Understand auto type deduction - 了解auto类型推断
条款二 了解auto类型推断 基础知识 除了一处例外,auto的类型推断与template一样.存在一个直接的从template类型推断到auto类型推断的映射 三类情况下的推断如下所示: // ca ...
- C++11特性——变量部分(using类型别名、constexpr常量表达式、auto类型推断、nullptr空指针等)
#include <iostream> using namespace std; int main() { using cullptr = const unsigned long long ...
- item 2: 理解auto类型的推导
本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 如果你已经读过item 1的模板类型推导,你已经知道大部分关于au ...
- C++ 自动类型推断
C++语言提供了自动类型推断的机制,用于简化代码书写,这是一种很不错的特性,使用auto和decltype都可以完成自动类型推断的工作,而且都工作在编译期,这表示在运行时不会有任何的性能损耗. 一.a ...
- 《Effective Modern C++》翻译--条款2: 理解auto自己主动类型推导
条款2: 理解auto自己主动类型推导 假设你已经读过条款1关于模板类型推导的内容,那么你差点儿已经知道了关于auto类型推导的所有. 至于为什么auto类型推导就是模板类型推导仅仅有一个地方感到好奇 ...
- C++11新特性:自动类型推断和类型获取
声明:本文是在Alex Allain的文章http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-functi ...
- (2)左右值初探与auto类型说明符
这篇文章的起因是下面这两段代码,出自<C++ primer 5th>中文版P62页: auto &h =42;//错误,不能为非常量引用绑定字面值 const auto & ...
- WebKit Web Inspector增加覆盖率分析和类型推断功能
WebKit中的Web Inspector(Web检查器)主要用于查看页面源代码.实时DOM层次结构.脚本调试.数据收集等,日前增加了两个十分有用的新功能:覆盖率分析和类型推断.覆盖率分析工具能够可视 ...
随机推荐
- luogu3645 [Apio2015]雅加达的摩天大楼 (分块+dijkstra)
我们是想跑最短路的 我们有两种建图方式: 1.对于每个doge i,连向B[j]==B[i]+P[i]*k ,k=..,-2,-1,0,1,2,... ,边权=|k|,这样连的复杂度是$O(N\sum ...
- Java -- JDBC_利用反射及 JDBC 元数据编写通用的查询方法
先利用 SQL 进行查询,得到结果集: 利用反射创建实体类的对象:创建对象: 获取结果集的列的别名: 再获取结果集的每一列的值, 结合 3 得到一个 Map,键:列的别名,值:列的值: 再利用反射为 ...
- 文件拷贝, 使用 BIO,NIO的对比,四种写法性能分析。
测试环境: jdk 1.7 + 2G内存 测试代码基本上复制了: http://blog.csdn.net/tabactivity/article/details/9317143 1 2 3 4 5 ...
- C++11并发——多线程lock_gurad ,unique_lock (三)
http://www.cnblogs.com/haippy/p/3346477.html struct defer_lock_t {}; 该类型的常量对象 defer_lock(defer_lock ...
- call_user_func 和 call_user_func_array用法
说明 call_user_func 和 call_user_func_array 相同:都可以调用函数和类内部的函数,不同:不同的是传递的参数不同,前者是一个参数一个参数传递, 后者是传递array参 ...
- centos7使用kubeadm配置高可用k8s集群
CountingStars_ 关注 2018.08.12 09:06* 字数 464 阅读 88评论 0喜欢 0 简介 使用kubeadm配置多master节点,实现高可用. 安装 实验环境说明 实验 ...
- sklearn多分类问题
sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...
- Hadoop基础-配置历史服务器
Hadoop基础-配置历史服务器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Hadoop自带了一个历史服务器,可以通过历史服务器查看已经运行完的Mapreduce作业记录,比 ...
- MySQL 5.6版本内存占用过高的解决办法
最近在阿里云购买了一台云服务器,因为是自己测试玩的,所以配置按最低的来了,1G内存,然后啪啪啪(指键盘声音)的安装了JDK,Tomcat,MySQL(5.6)等一系列环境,开始很爽,然后噩梦开始了: ...
- javascript 拖拽事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...