1. 类型推导 auto decltype

1.1 auto

定义变量的时候,通过初始值推断类型

auto i = 42;		// 推断i的类型为int

auto 会忽略顶层const

const int ci = 42;
auto b = ci;		// b的类型是int
const auto f = ci;	// 可以显式的指明const

auto和引用

auto &g = ci;	// g的推断类型为cont int&

auto比较有意义的用法是在些一些比较复杂类型时可以方便一点,比如

std::vector<int> ivec = {1, 2, 3, 4, 5};
for (std::vector<int>::const_iterator it = ivec.begin(); 
     it != ivec.end(); ++it) {
    std::cout << *it << std::endl;
}

it 迭代器的类型名比较复杂,可以使用 auto 类型推导

for (auto it = ivec.begin(); it != ivec.end(); ++it) {
    std::cout << *it << std::endl;
}

// 更简单的遍历,使用范围for
for (auto it : ivec) {
    std::cout << it << std::endl;
}

auto 类型推导不是万能的,不应该过度依赖于自动类型推断,它只是使编码更方便,在使用auto自动类型推断时应该做到“心中有数”。

1.2 decltype

decltype 的功能和 typeof 类似,计算表示式的类型

decltype(表达式)
int i  = 42;
auto j = 1;
decltype(i + j) k;		// k 的类型是int
decltype(f()) s = x;	// s 的类型是函数f() 返回值的类型

decltype 类型计算,它不会执行表达式

decltype 处理顶层 const 和引用的方式与 auto 不同,如果使用的表达式是一个变量,则返回该变量的类型(包括顶层const和引用在内)

const int ci = 0, &cj = ci;
decltype(ci) x = 0;			// x的类型是 const int
decltype(cj) y = x;			// y的类型是 const int&, y绑定到变量x
decltype(cj) z;				// 错误, z是一个引用,必须初始化

decltype 和引用

int i = 42, *p = &i, &r = i;
decltype(r + 0) b;		// 加法的结果是int,所以b是一个未初始化的int
decltype(*p) c;			// 错误,c是 int&, 必须初始化

如果表达式的内容是一个解引用操作,则 decltype 将得到引用类型。

如果表达式是加上了括号的变量,结果将是引用

decltype((i)) d;	// d是int&, 必须初始化
decltype(i) e;		// e 是 int类型

1.3 尾置类型返回

一个模板函数的代码

template<typename R, typename T, typename U>
R add(T x, U y) {
    return x + y;
}

在使用这个模板函数必须指定三个类型参数

int sum =  add<int, int, int>(1, 2);

使用尾指类型返回可以简化

template<typename T, typename U>
auto add(T x, U y)-> decltype(x+y) {
    return x + y;
}

一个错误的代码

template<typename T, typename U>
decltype(x+y) add(T x, U y) {
    return x + y;
}

上面的代码使用 decltype(x+y) 计算返回类型,但是在使用 x y 之前,编译器并不知它值,根本没法计算类型。