本文是C++11新特性介绍的第二部分,涉及到C++11这次更新中较为重要的特性类型推断(auto)与类型获取(decltype)。
简单的类型推断 C++11新标准中增加了auto类型说明符,可以让编译器帮我们分析表达式的类型。1
2
3
4
5
6
7
8
9
double val1 = 1.1 , val2 = 2.2 ;
auto sum = val1 + val2;
auto val3 = 0.3 , *p = &val3;
double val6 = 1.6 , &rval6 = val6;
auto aval6 = rval6;
aval6 = 6.0 ;
cout <<"test simple auto:\n" <<val1<<'\t' <<val2<<'\t' <<sum<<'\t' <<val3<<'\t' <<p<<'\t' <<val6<<'\t' <<rval6<<'\t' <<aval6<<endl ;
需要注意的是: 1.使用auto定义的变量必须有初始值,不然无法进行类型推断 2.在同一条语句中使用auto定义的变量,其基础类型必须一致
const 和 auto auto在进行类型推断时,一般会忽略顶层const(top-level const),而保留底层const(low-level const)。如果想要保留顶层const,则必须显式的在auto前添加const指示符。 所谓顶层const,指的是当前的数据类型本身是常量,如double,int或者相关的指针本身是常量; 而底层const,指的是如指针、引用等复合类型,其所指向的数据类型是常量。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const int val7 = 1 , &rval7 = val7;
auto aval7 = val7;
aval7 = 7 ;
auto aval8 = rval7;
aval8 = 8 ;
auto aval9 = &val6;
*aval9 = 9 ;
auto aval10 = &val7;
const auto aval11 = val7;
auto &aval12 = val7;
auto &aval13 = val6;
aval13 = 13.0 ;
const auto &aval15 = 15 ;
cout <<"test auto and const:\n" <<val7<<'\t' <<rval7<<'\t' <<aval7<<'\t' <<aval8<<'\t' <<*aval9<<'\t' <<aval10<<'\t' <<aval11<<'\t' <<aval12<<'\t' <<aval13<<'\t' <<aval15<<endl ;
当定义一个auto的引用时,顶层const被保留,如上述测试代码中的aval12所示。另外,输出的结果中,有些数值可能和预想的不太一样,可以思考一下是为什么^_^(Tips:和引用有关)。
类型获取 decltype decltype(expr)可以获得expr表达式对应的类型,并且不会对expr具体求值。1
2
3
4
5
6
7
int d ()
{
cout <<"This function shouldn't be called." <<endl ;
return 17 ;
}
decltype (d()) dval17 = 15.2 ;
cout <<"test decltype:\n" <<dval17<<endl ;
decltype 与 const decltype处理const的方式与auto不同。 1.如果decltype中的表达式是一个变量,那么返回该变量的类型(包括顶层const) 2.如果decltype中的表达式不是变量,则返回该表达式结果对应的类型。 看上去没啥区别?其实这里的规则导致了decltype(r+0) decltype((i))这种诡异的写法。还是具体看代码吧。1
2
3
4
5
6
7
8
9
10
11
decltype (val7) val18 = 0 ;
decltype (rval7) val19 = val18;
cout <<"test decltype and const:\n" <<val18<<'\t' <<val19<<endl ;
decltype (rval6 + 0 ) val22;
decltype (val6) val24;
cout <<"test decltype and reference:\n" <<val22<<'\t' <<val24<<endl ;
上面代码中需要注意的地方有: 1.val21处,如果decltype中的表达式是一个解引用操作,那么将得到一个引用类型,所以必须初始化。 2.val22处,rval6是一个引用类型(double&),如果我们需要获得这个引用的基础类型(即double),那么使用rval6 + 0这样一个表达式,显然这个表达式的结果将不是引用了。 3.val23和val24处,如果decltype中的变量加上了括号,那么就会被当作表达式处理;而变量是一种可以作为左值被赋值的特殊表达式,因此decltype对于这种带括号的变量(val23处),就会得到一个引用类型。
使用 auto 缩写类型 1
2
3
string name = "Yubo" ;
auto length = name.size();
cout <<"test auto with complex type:\n" <<length<<endl ;
不用费劲写string::size_type了^o^
使用 auto 简化声明 声明指向数组的指针总是一件令人痛苦的事情:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int val25[3 ][4 ] = {
{0 , 1 , 2 , 3 },
{4 , 5 , 6 , 7 },
{8 , 9 , 10 , 11 }
};
cout <<"test auto to simplify type:\n" ;
cout <<"old way:\n" ;
for (int (*p)[4 ] = val25; p != val25 + 3 ; p++)
{
for (int *q = *p; q != *p + 4 ; q++)
{
cout <<*q<<'\t' ;
}
cout <<'\n' ;
}
有了auto之后,我们可以像下面这样清爽:1
2
3
4
5
6
7
8
9
cout <<"new way:\n" ;
for (auto ap = val25; ap != val25 + 3 ; ap++)
{
for (auto aq = *ap; aq != *ap + 4 ; aq++)
{
cout <<*aq<<'\t' ;
}
cout <<'\n' ;
}
使用 decltype 简化函数返回类型 如果我们已经知道某个函数会返回什么对象,然而这个对象又是一个类型复杂不好写的对象,那么decltype就可以派上用场了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int odd[] = {1 , 3 , 5 , 7 , 9 };
int even[] = {0 , 2 , 4 , 6 , 8 };
decltype (odd) *get_odd_or_even(int i)
{
return (i % 2 ) ? &odd : &even;
}
auto val26 = get_odd_or_even(1 );
cout <<"test decltype to simplify func return type:\n" ;
for (auto p = begin(*val26); p != end(*val26); p++)
{
cout <<p<<' ' <<*p<<'\t' ;
}
cout <<endl ;
使用 auto 动态分配内存 auto可以和new配合,来动态分配内存,并进行初始化。1
2
3
4
5
6
7
8
9
10
11
12
13
14
auto val27 = new auto (val24);
auto val28 = new auto (name);
cout <<"test auto to new object with a given obj:\n" ;
cout <<*val27<<'\t' <<*val28<<'\t' <<val28<<'\t' <<&name<<endl ;
auto val29 = new auto (odd);
for (auto p = *val29; p != *val29 + 5 ; p++)
{
cout <<p<<' ' <<*p<<'\t' ;
}
cout <<endl ;
int *val31 = new int [10 ]{0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 };
cout <<val31<<'\t' <<val31[0 ]<<"to" <<val31[9 ]<<endl ;
这里有几处需要留意的地方: 1.val29处,只是new出了一个指向数组的指针,并没有复制数组的值。因此在下面循环中打印出的p值(地址)和odd数组的地址是一样的。 2.不可以使用auto来分配一个动态数组。这是因为使用new分配数组时,不支持圆括号的初始化方式,只支持花括号的列表初始化方式。
总结 1.可以使用auto说明符,让编译器帮我们推断类型。 2.auto在进行类型推断时,一般会忽略顶层const(top-level const),而保留底层const(low-level const)。 3.decltype(expr)可以获得expr表达式对应的类型,并且不会对expr具体求值。 4.decltype(rval + 0)可以获得值类型(非引用),decltype(*p)获得引用类型,decltype((val))获得引用类型。 5.使用auto可以缩写一些复杂难写的类型。 6.使用auto可以简化类型声明,尤其是数组和指针混合的声明。 7.在知道某一函数会返回什么对象时,可以使用decltype可以简化函数返回类型。 8.auto和new可以配合以动态分配内存,但是不能用于动态分配数组。