C++拾趣——C++11的语法糖auto
C++是一种强类型的语言,比如变量a,如果声明它是整型,则之后只能将它作为整型来用。这和其他弱类型的语言有很大的区别,比如python中,我们可以让a在第一行是个整型,第三行是一个字符串。(转载请指明出于breaksoftware的csdn博客)
a = 3
print a
a = "3"
print a
C++代码在声明一个变量时就已经明确指定了它的类型。这样在编译器给它分配内存时,就知道分配出什么多大的空间。从这个角度来说,C++的语法是站在编译器实现的角度设计的。然而随着编程技术的普及,大家都希望代码写出来是给人看的,而不是给机器看的。但是在C++代码的一些场景下,有一定基础的程序员都可以通过右值推导出左值变量类型时,编译器还是要求写明类型,这无疑加重了程序员的负担。比如下面的代码
std::vector<int> vec_int;
……
std::vector<int>::iterator iter = vec_int.begin();
如果不看变量iter的类型声明,我们也可以通过vector的begin()函数返回值类型推导出变量的类型。这样的推导编译器也完全可以完成。但是在C++11之前的标准中,我们也只能这种笨拙的方式去声明。因为老的标准没要求编译器完成这样的工作,而且即使各大编译器厂商“超前”的完成了,也没有一种统一的途径可以表达出来。
于是在C++11的标准中引入了auto关键字,它让上述类型推导通过统一的标准表达出来。我们可以使用auto去声明一个变量,但是这并不意味着C++增加了一个auto类型——一个可以表示任意类型的类型。因为强类型特性是不会变的,变的只是编译器,它变的更加高级——推导出确定的类型。这儿特别需要指出的是:auto关键字并不是在预编译过程中被替换成确定类型的。大家可以开启VS2017的“生成预编译文件”选项来验证这点。
std::vector<int> vec_int;
……
auto iter = vec_int.begin();
上述代码中auto便将std::vector<int>::iterator替换了,这在一定程度上降低了程序员的负担。
在C++11标准推出之前,我们可以使用别名来减少负担。比如会在一个共有的头文件中做如下定义:
typedef std::vector<int> VecInt;
typedef VecInt::iterator VecIntIter;
typedef VecInt::const_iterator VecIntCIter;
然后在代码中使用上述别名
VecInt vec_int;
……
VecIntIter = vec_int.begin();
当然这不失为一种方法,但是这就要求每定义一个容器,都要别名它相应的迭代器。auto的出现帮我们省掉了这些繁琐且意义不大的书写。
auto是个非常好的设计,但是也不可毫无原则的滥用。
作为C++标准,要求auto变量在声明时要被初始化。该初始化操作其实就是指定了其真实类型。一般我们可以使用表达式来初始化一个变量,也可以使用字面值、字面量、常量或者一个明确类型的变量。不管我们使用哪种方式,都希望auto的推导不会产生“歧义”。比如
auto i = 0x01;
在VS2017中,我们将其类型打印出来(typeid(i).name())是int。那可能会问,为什么不是unsigned int,long等呢?现在我们换个字面值(8个f)
auto i = 0xffffffff;
此时,编译器就会认为类型为unsigned int。我们再修改字面值(15个f,1一个0)
auto i = 0x0fffffffffffffff;
编译器推导出的类型是__int64。我们再将类型改成(16个f)
auto i = 0xffffffffffffffff;
编译器推导出的类型是unsigned __int64。
可以见得,在使用字面值或者字面量初始化auto变量时往往会产生“误解”。所以一旦我们自己对推导产生疑问时,最好使用明确的类型来定义变量。
其次,不要寄希望于编译器可以通过构造函数隐式转换推导类型。比如下面的初始化方式,我们可能会认为变量类型是std::string。
auto i = "which type?";
但是真实的推导类型是char const *。为什么呢?因为当我们使用
std::string i = "which type?";
赋值符将会触发basic_string类构造函数隐式转换(《C++拾趣——类构造函数的隐式转换》)
basic_string(_In_z_ const _Elem * const _Ptr)
: _Mybase()
{ // construct from [_Ptr, <null>)
_Tidy_init();
assign(_Ptr);
}
这是由于左值已经指定类型才会发生的。所以如果使用auto定义一个字面量,其类型是char const *。鉴于理解这样的过程需要掌握一定的C++基础知识,所以我也不建议在这个场景下使用auto去定义变量。
综上所述,除了在模板中必要的地方使用auto外,其他地方都需要可以从右值一眼看出类型,否则就会产生理解上的歧义或者困扰。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Linux中将txt导入到mysql的办法教程
- 深入了解NumPy 高级索引
- CentOS7设置jar应用程序开机启动的办法
- linux系统 java环境变量的配置办法
- 在Linux中怎么轻松删除源安装的软件包
- Linux中selinux基础配置教程详解
- 怎么禁用 Ubuntu 服务器中终端欢迎消息中的广告
- Laravel5.1 框架响应基本用法实例分析
- 在Linux中怎么一次重命名多个文件详解
- python调用私有属性的方法总结
- PHP+MySQL实现在线测试答题实例
- Python异常处理机制结构实例解析
- PHP字符串与数组处理函数用法小结
- 详解Flask前后端分离项目案例
- Laravel5.1 框架表单验证操作实例详解