C++拾趣——C++11的语法糖auto

时间:2022-06-17
本文章向大家介绍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外,其他地方都需要可以从右值一眼看出类型,否则就会产生理解上的歧义或者困扰。