转自 程序喵大人的一文吃透 C++11 中 auto 和 decltype 知识点
以下是正文部分,我对排版和错别字进行了一点修改
关于 C++11 新特性,最先提到的肯定是类型推导,C++11 引入了 auto 和 decltype 关键字,使用它们可以在编译期就推导出变量或者表达式的类型,方便开发者编码的同时也简化了代码。
auto 关键字
auto 可以让编译器在编译期就推导出变量的类型,话不多说上代码:
auto a = 10; // 10 是 int型,可以自动推导出 a 是 int
int i = 10;
auto b = i; // b 是 int 型
auto d = 2.0; // d 是 double 型
这就是 auto 的基本用法,可以通过 = 右边的类型推导出变量的类型。
auto 推导规则
直接看代码
代码1:
int i = 10;
auto a = i, &b = i, *c = &i; // a 是 int,b 是 i 的引用,c 是 i 的指针,auto 就相当于 int
/// @note 错误的情况
auto d = 0, f = 1.0; // error,0 和 1.0 类型不同,对于编译器有二义性,没法推导
auto e; // error,使用 auto 必须马上初始化,否则无法推导类型
代码2:
void func(auto value) { } // error,auto 不能用作函数参数
class A {
auto a = 1; // error,在类中 auto 不能用作非静态成员变量
static auto b = 1; // error,这里与 auto 无关,正常 static int b = 1也不可以
static const auto int c = 1; // ok
};
void func2() {
int a[10] = { 0};
auto b = a; // ok
auto c[10] = a; // error,auto 不能定义数组,可以定义指针
vector < int > d;
vector < auto > f = d; // error,auto 无法推导出模板参数
}
从以上代码,我们可以得到如下
auto 的限制
- auto 的使用必须马上初始化,否则无法推导出类型
- auto 在一行定义多个变量时,各个变量的推导不能产生二义性,否则编译失败
- auto 不能用作函数参数
- 在类中 auto 不能用作非静态成员变量
- auto 不能定义数组,可以定义指针
- auto 无法推导出模板参数
涉及 const / volatile 的推导规则
再看这段代码:
int i = 0;
auto * a = & i; // a 是int*
auto & b = i; // b 是int&
auto c = b; // c 是int,忽略了引用
const auto d = i; // d 是 const int
auto e = d; // e 是 int
const auto& f = e; // f 是const int&
auto & g = f; // g 是const int&
注意,下面的 cv 是指 const 和 volatile
- 在不声明为引用或指针时,auto 会忽略等号右边的引用类型和 cv 限定
- 在声明为引用或者指针时,auto 会保留等号右边的引用和 cv 属性
什么时候使用 auto
这里没有绝对答案,只能说一下我自己的理解,个人认为在不影响代码可读性的前提下尽可能使用 auto 是蛮好的,复杂类型就使用 auto,int、double这种就没有必要使用 auto 了吧,看下面这段代码:
auto func = [&] {
cout << "xxx";
}; // 对于 func 你难道不使用 auto 吗,反正我是不关心 lambda 表达式究竟是什么类型。
auto asyncfunc = std::async(std::launch::async, func);
// 对于 asyncfunc 你难道不使用 auto 吗,我是懒得写 std::futurexxx 等代码,而且我也记不住它返回的究竟是什么...
decltype 关键字
上面介绍 auto 用于推导变量类型,而 decltype 则用于推导表达式类型,这里只用于编译器分析表达式的类型,表达式实际不会进行运算,上代码:
int func() { return 0; }
decltype(func()) i; // i 为 int 类型
int x = 0;
decltype(x) y; // y 是 int 类型
decltype(x + y) z; // z 是 int 类型
注意:decltype 不会像 auto 一样忽略引用和 cv 属性,decltype 会保留表达式的引用和 cv 属性
cont int &i = 1;
int a = 2;
decltype(i) b = 2; // b 是 const int&
decltype 推导规则
对于 decltype(exp) 有
- exp 是表达式,decltype(exp) 和 exp 类型相同
- exp 是函数调用,decltype(exp) 和函数返回值类型相同
- 其它情况,若 exp 是左值,decltype(exp) 是 exp 类型的左值引用
int a = 0, b = 0;
decltype(a + b) c = 0; // c 是 int,因为 (a+b) 返回一个右值
decltype(a += b) d = c;// d 是 int&,因为 (a+=b) 返回一个左值
d = 20;
cout << "c " << c << endl; // 输出 c 20
关于左值和右值知识点后续程序喵会介绍的,请关注哦~
auto 和 decltype 的配合使用【返回值类型后置】
auto 和 decltype 一般配合使用在推导函数返回值的类型问题上。
下面这段代码
template < typename T, typename U >
return_value add(T t, U u) { // t 和 v 类型不确定,无法推导出return_value类型
return t + u;
}
上面代码由于 t 和 u 类型不确定,那如何推导出返回值类型呢,我们可能会想到这种
template < typename T, typename U >
decltype(t + u) add(T t, U u) { // t 和 u 尚未定义
return t + u;
}
这段代码在 C++11 上是编译不过的,因为在 decltype(t +u) 推导时,t 和 u 尚未定义,就会编译出错,所以有了下面的叫做返回类型后置的配合使用方法:
template < typename T, typename U >
auto add(T t, U u) -> decltype(t + u) {
return t + u;
}
返回值后置类型语法就是为了解决函数返回值类型依赖于参数但却难以确定返回值类型的问题。