C++11 auto类型推导

【1】静态类型、动态类型与类型推导

静态类型和动态类型的主要区别在于对变量进行类型检查的时间点:

静态类型,类型检查主要发生在编译阶段;

动态类型,类型检查主要发生在运行阶段。

类型推导示例如下:

 1 int main()
 2 {
 3     double foo();
 4     auto x = foo();    // x的类型为double
 5     auto y = 1;        // y的类型为int
 6     struct m { int i; } str;
 7     auto str1 = str;  // str1的类型是struct m
 8     auto z;           // 无法推导,无法通过编译
 9     z = x;
10 }

尤其值得注意的是变量z,这里使用auto关键字来“声明”z,但不立即对其进行定义,此时编译器则会报错。

这跟通过其他关键字(除去引用类型的关键字)先声明后定义变量的使用规则是不同的。

auto声明的变量必须被初始化,以使编译器能够从其初始化表达式中推导出其类型。

从这个意义上来讲,auto并非一种“类型”声明,而是一个类型声明时的“占位符”,编译器在编译时期会将auto替代为变量实际的类型。

【2】auto的优势

auto的优势:

(1)auto推导的一个最大优势就是在拥有初始化表达式的复杂类型变量声明时简化代码。请意会,很简单不做示例。

(2)可以免除程序员在一些类型声明时的麻烦,或者避免一些在类型声明时的错误。请意会,很简单不做示例。

(3)就是其“自适应”特性能够在一定程度上支持泛型的编程。请意会,很简单不做示例。

(4)auto还会在一些情况下取得意想不到的好效果。

示例如下:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 #define MAX1(a, b) ((a) > (b)) ? (a) : (b)
 5 #define MAX2(a, b, max) { 
 6     auto _a = (a); 
 7     auto _b = (b); 
 8     max = (_a > _b) ? _a : _b; }
 9 
10 int main()
11 {
12     int m1 = MAX1(1 * 2 * 3 * 4, 5 + 6 + 7 + 8);
13     cout << m1 << endl;  // 26
14     int m2 = 0;
15     MAX2(1 * 2 * 3 * 4, 5 + 6 + 7 + 8, m2);
16     cout << m2 << endl;  // 26
17 }

定义了两种类型的宏Max1和Max2。两者作用相同,都是求a和b中较大者并返回。

前者采用传统的三元运算符表达式,这可能会带来一定的性能问题。

因为,a或b在三元运算符中都出现了两次,那么,无论是取a还是取b,其中之一都会被运算两次。

而在Max2中,我们将a和b都先算出来,再使用三元运算符进行比较,就不会存在这样的问题了。

在传统的C++98标准中,由于a和b的类型无法获得,所以我们无法定义Max2这样高性能的宏。

而新的标准中的auto则提供了这种可行性。

【3】auto的应用细则

(1)auto可以与指针和引用结合起来使用。示例如下:

 1 int x; 
 2 int* y = &x; 
 3 double foo();
 4 int& bar();
 5 auto* a = &x;  // int* 
 6 auto & b = x;  // int& 
 7 auto c = y;    // int* 
 8 auto * d = y;  // int* 
 9 auto * e = &foo(); // 编译失败, 指针不能指向一个临时变量
10 auto & f = foo();  // 编译失败, nonconst的左值引用不能和一个临时变量绑定 
11 auto g = bar();    // int
12 auto & h = bar();  // int& 

(2)auto可以与cv限制符一起使用,不过声明为auto的变量并不能从其初始化表达式中“带走”cv限制符。

volatile和const代表了变量的两种不同的属性:易失的和常量的。

在C++标准中,它们常常被一起叫作cv限制符(cv-qualifier)。示例如下:

1 double foo() {};
2 float* bar() {};
3 const auto a = foo();      // a: const double 
4 const auto & b = foo();    // b: const double& 
5 volatile auto * c = bar(); // c: volatile float * 
6 auto d = a;                // d: double
7 auto & e = a;              // e: const double &
8 auto f = c;                // f: float * volatile
9 auto & g = c;              // g: volatile float * &

可以通过非cv限制的类型初始化一个cv限制的类型,如变量a、b、c所示。

不过通过auto声明的变量d、f却无法带走a和f的常量性或者易失性。

这里的例外还是引用,可以看出,声明为引用的变量e、g都保持了其引用的对象相同的属性(事实上,指针也是一样的)。

(3)同一个赋值语句中,auto可以用来声明多个变量的类型,不过这些变量的类型必须相同。

如果这些变量的类型不相同,编译器则会报错。

事实上,用auto来声明多个变量类型时,只有第一个变量用于auto的类型推导,然后推导出来的数据类型被作用于其他的变量。

所以不允许这些变量的类型不相同。示例如下:

1 auto x = 1, y = 2;  // x和y的类型均为int 
2 // m是一个指向const int类型变量的指针, n是一个int类型的变量 
3 const auto* m = &x, n = 1;
4 auto i = 1, j = 3. 14f;      // 编译失败
5 auto o = 1, &p = o, *q = &p; // 从左向右推导

(4)初始化列表,以及new,都可以使用auto关键字。示例如下:

1 #include <initializer_list>
2 auto x = 1;
3 auto x1( 1);
4 auto y {1};           // 用于初始化列表的auto
5 auto z = new auto(1); // 用于new

good good study, day day up.

顺序 选择 循环 总结

原文地址:https://www.cnblogs.com/Braveliu/p/12242011.html