int temp = 110; auto *a = &temp; // 变量 a 的数据类型为 int*,因此 auto 关键字被推导为 int 类型 auto b = &temp; // 变量 b 的数据类型为 int*,因此 auto 关键字被推导为 int* 类型 auto &c = temp; // 变量 c 的数据类型为 int&,因此 auto 关键字被推导为 int 类型 auto d = temp; // 变量 d 的数据类型为 int,因此 auto 关键字被推导为 int 类型
带 const 限定的变量,使用 auto 进行类型推导的例子:
1 2 3 4 5
int tmp = 250; constauto a1 = tmp; // 变量 a1 的数据类型为 const int,因此 auto 关键字被推导为 int 类型 auto a2 = a1; // 变量 a2 的数据类型为 int,a2 没有声明为指针或引用,因此 const 属性被去掉,auto 被推导为 int constauto &a3 = tmp; // 变量 a3 的数据类型为 const int&,a3 被声明为引用,因此 const 属性被保留,auto 关键字被推导为 int 类型 auto &a4 = a3; // 变量 a4 的数据类型为 const int&,a4 被声明为引用,因此 const 属性被保留,auto 关键字被推导为 const int 类型
auto 的限制
auto 关键字并不是万能的,在以下这些场景中不能完成类型推导: (1)不能作为函数参数使用。因为只有在函数调用的时候才会给函数参数传递实参,auto 要求必须要给修饰的变量赋值,因此二者矛盾。
1 2 3 4
intfunc(auto a, auto b)// error { cout << "a: " << a << ", b: " << b << endl; }
classTest { public: string text; staticconstint value = 110; };
intmain() { int x = 99; constint &y = x; decltype(x) a = x; // 变量 a 被推导为 int 类型 decltype(y) b = x; // 变量 b 被推导为 const int & 类型 decltype(Test::value) c = 0; // 变量 c 被推导为 const int 类型 Test t; decltype(t.text) d = "hello, world"; // 变量 d 被推导为 string 类型
// 函数声明 intfunc_int(); // 返回值为 int int& func_int_r(); // 返回值为 int& int&& func_int_rr(); // 返回值为 int&& constintfunc_cint(); // 返回值为 const int constint& func_cint_r(); // 返回值为 const int& constint&& func_cint_rr(); // 返回值为 const int&& const Test func_ctest(); // 返回值为 const Test
intmain() { // decltype类型推导 int n = 100; decltype(func_int()) a = 0; // 变量 a 被推导为 int 类型 decltype(func_int_r()) b = n; // 变量 b 被推导为 int& 类型 decltype(func_int_rr()) c = 0; // 变量 c 被推导为 int&& 类型 decltype(func_cint()) d = 0; // 变量 d 被推导为 int 类型 decltype(func_cint_r()) e = n; // 变量 e 被推导为 const int & 类型 decltype(func_cint_rr()) f = 0; // 变量 f 被推导为 const int && 类型 decltype(func_ctest()) g = Test(); // 变量 g 被推导为 const Test 类型
return0; }
函数 func_cint() 返回的是一个纯右值(在表达式执行结束后不再存在的数据,也就是临时性的数据)。对于纯右值而言,只有类类型可以携带 const、volatile 限定符,除此之外需要忽略掉这两个限定符,因此推导出的变量 d 的类型为 int 而不是 const int。 (3)表达式是一个左值,或者被括号 () 包围,使用 decltype 推导出的是表达式类型的引用(如果有 const、volatile 限定符不能忽略)。
intmain() { const Test obj; // 带有括号的表达式 decltype(obj.num) a = 0; // obj.num 为类的成员访问表达式,符合场景 1,因此 a 的类型为 int decltype((obj.num)) b = a; // obj.num 带有括号,符合场景 3,因此 b 的类型为 const int& // 加法表达式 int n = 0, m = 0; decltype(n + m) c = 0; // n + m 得到一个右值,符合场景 1,因此 c 的类型为 int decltype(n = n + m) d = n; // n = n + m 得到一个左值 n,符合场景 3,因此 d 的类型为 int&
// R->返回值类型, T->参数1类型, U->参数2类型 template <typename R, typename T, typename U> R add(T t, U u) { return t + u; }
intmain() { int x = 520; double y = 13.14; // auto z = add<decltype(x + y), int, double>(x, y); auto z = add<decltype(x + y)>(x, y); // 简化后的写法 cout << "z: " << z << endl;
return0; }
关于返回值,从上面的代码可以推断出和表达式 t + u 的结果类型是一样的,因此可以通过 decltype 进行推导,关于模板函数的参数 t 和 u 可以通过实参自动推导出来,因此在程序中可以不写。虽然通过上述方式问题被解决了,但是解决方案有点过于理想化,因为对于调用者来说,是不知道函数内部执行了什么样的处理动作的。 因此,想解决这个问题就得直接在 add 函数上做文章,先来看第一种写法:
1 2 3 4 5
template <typename T, typename U> decltype(t + u) add(T t, U u) { return t + u; }
在编译器中将这几行代码改出来后直接报错,因为 decltype 中的 t 和 u 都是函数参数,这样写相当于变量还没有定义就使用了,这时候变量还不存在。在 C++11 中增加了返回类型后置语法,将 decltype 和 auto 结合起来完成返回类型的推导。其语法格式如下: