在传统 C 和 C++ 中,参数的类型都必须明确定义,这其实对我们快速进行编码没有任何帮助,尤其是当我们面对一大堆复杂的模板类型时,必须明确的指出变量的类型才能进行后续的编码,这不仅拖慢我们的开发效率,也让代码变得又臭又长。
C++ 11引入了 auto
和 decltype
这两个关键字实现了类型推导,让编译器来操心变量的类型。这使得 C++ 也具有了和其他现代编程语言一样,某种意义上提供了无需操心变量类型的使用习惯。
auto
auto
是一个 C 语言的关键字,始终作为一个存储类型的指示符存在,与 register
并存。C++ 11重新定义了 auto
的含义,使之成为一个能够让编译器根据初始值的类型推断变量的类型。使用 auto
关键字进行类型推导的最为常见的一个例子就是迭代器。
void foo(vector<string>& temp) {
/*//老式写法,但是C++ 11不建议直接显示使用迭代器,所以建议使用 auto,自动类型推导
for(vector<string>::iterator it = temp.begin();it != temp.end();it++)
cout << *it << endl;*/
for (auto it = temp.begin(); it != temp.end(); it++)
cout << *it << endl;
}
一些其它的用法
/*
auto的自动类型推导,用于从初始化表达式中推断出变量的数据类型。
从这个意义上讲,auto并非一种“类型”声明,而是一个类型声明时的“占位符”,
编译器在编译时期会将auto替换为变量实际的类型。
*/
void test1() {
auto x = 1;
auto y = dou();
cout << "x 的类型为:" << typeid(x).name() << endl;
cout << "y 的类型为:" << typeid(y).name() << endl;
struct Node {
int id;
string name;
};
Node s1;
auto s2 = s1;
cout << "s2 的类型为:" << typeid(s2).name() << endl;
auto z = x;
cout << "z 的类型为:" << typeid(z).name() << endl;
cout << y << endl;
}
一些注意事项
void fun(auto x = 1) {} //auto函数参数,有些编译器无法通过编译
struct str{
auto var = 10; // 2: auto非静态成员变量,无法通过编译
};
auto z[3] = x; // 3: auto数组,无法通过编译
// 4: auto模板参数(实例化时),无法通过编译
vector<auto> x = {1};
decltype
decltype实际上有点像auto的反函数, auto可以让你声明一个变量,而decltype则可以从一个变量或表达式中得到其类型,如下:
#include <typeinfo>
#include <iostream>
#include <vector>
using namespace std;
int main() {
int i;
decltype(i) j = 0;
cout << typeid(j).name() << endl; // 打印出"i", g++表示integer
float a;
double b;
decltype(a + b) c;
cout << typeid(c).name() << endl; // 打印出"d", g++表示double
vector<int> vec;
typedef decltype(vec.begin()) vectype; // decltype(vec.begin()) 改名为 vectype
vectype k; // 这是auto无法做到的
//decltype(vec.begin()) k; // 这是auto无法做到的
for (k = vec.begin(); k < vec.end(); k++) {
// 做一些事情
}
enum { Ok, Error, Warning }flag; // 匿名的枚举变量
decltype(flag) tmp = Ok;
return 0;
}
补充说明:
const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1; // const int&& (1)
decltype(i) x2; // int (2)
decltype(a->x) x3; // double (3)
decltype((a->x)) x4; // double& (4)
在上述代码中 (4)decltype((a->x))
的类型是 double&
,这是因为 decltype
判别规律决定的。
对于decltype(e)
而言,其判别结果受以下条件的影响:
1. 如果 e 是一个标识符或者类成员的访问表达式,则 decltype(e) 就是 e 所代表的实体的类型。如果没有这种类型或者 e 是一个重载函数集,那么程序是错误的(上例中的 (2) 和 (3));
2. 如果 e 是一个函数调用或者一个重载操作符调用(忽略 e 外面的括号),那么 decltype(e) 就是该函数的返回类型(上例中的 (1));
3. 如果 e 不属于以上所述的情况,则假设 e 的类型是 T:当 e 是一个左值时,decltype(e) 就是 T&;否则(e是一个右值),decltype(e) 是 T(上例中的 (4) 即属于这种情况。在这个例子中,e 实际是 (a->x),由于有这个括号,因此它不属于前面两种情况,所以应当以本条作为判别依据。而 (a->x) 是一个左值,因此会返回 double &)。
追踪返回类型
C++ 11新增了一种函数声明的语法:在函数名和参数列表后面指定返回值类型:
double f1(double, int);
auto f2(double, int) -> double;
就常规函数而言,这种新语法似乎在倒退,但是通过 auto
与 decltype
结合有意想不到的方法,如下所示
//在传统 C++ 中我们必须这么写
template<typename R, typename T, typename U>
R add(T x, U y) {
return x + y;
}
//使用 auto 于 decltype 可以方便操作
template<typename T, typename U>
auto add(T x, U y) -> decltype(x + y) {
return (x + y);
}
这里解决的问题就是,在编译器遇到 add
的参数列表前 T
, U
还不在作用域内,因此必须在参数列表后使用 decltype
才可以。
上述代码在使用上产生了一些疑问,和学长讨论了一个下午,因为模板在使用的过程中当传入的数据与显示声明的数据类型相同时,会有优先级发生了冲突出现了二义性,具体的使用情况如下:
auto ans1 = add<double, double>(1.0, 1.0); //产生二义性,错误
auto ans2 = add<double, double>(1, 1); //正确
auto ans3 = add<int, int>(1, 1); //错误,产生二义性
auto ans4 = add<int, int>(1.0, 1.0); //正确
auto ans5 = add<double, int>(1.0, 1); //错误