auto varname = value;
decltype(exp) varname [= value];
稍微解释一下,看不懂没关系,耐心往下看你就会明白了:
varname 表示变量名,value 表示赋给变量的值,exp 表示一个表达式,方括号[ ]表示可有可无。
auto 和 decltype 都会自动推导出变量 varname 的类型,但是他们都推导方式有些区别:
闲话:
auto 将变量的类型和初始值绑定在一起,而 decltype 将变量的类型和初始值分开;虽然 auto 的书写更加简洁,但 decltype 的使用更加灵活。
先来一个简单的例子:
#include
using namespace std;
int main()
{
auto n1 = 5;//必须初始化,不然会报错,因为是根据初始值推导出变量的类型的
decltype(10)n2;//没有初始化,可以不初始化,因为是根据()里面的内容推导出来的类型
decltype(10)n3 = 100;//初始化了。
auto str1 = "Gentle";
decltype(str1) str2 = "QQ845264718";
cout << "Hello world" << endl;
}
「cv 限定符」是 const 和 volatile 关键字的统称:
注意:
在推导变量类型时,auto 和 decltype 对 cv 限制符的处理是不一样的。decltype 会保留 cv 限定符,而 auto 有可能会去掉 cv 限定符。
auto 关键字对 cv 限定符的推导规则:
类型推导关键字对const限定符的推导例子:
#include
using namespace std;
int main()
{
//非指针非引用类型
const int n1 = 100;
auto n2 = 10;
n2 = 99;
decltype(n1) n3 = 20;
//n3 = 5; 这里会报错:不能给常量赋值 表达式必须是可以修改的左值 证明:n3 是 const int 类型,跟n1类型一致。
//指针类型 指向常量的指针 指针指向可以改,指针指向的值不可以改
const int *p1 = &n1;
auto p2 = p1;
//*p2 = 66; 这里会报错:不能给常量赋值 表达式必须是可以修改的左值 证明p2 的类型和p1一样是 const int*。
decltype(p1) p3;
//*p3 = 19; 这里会报错:不能给常量赋值 表达式必须是可以修改的左值 证明p3 的类型和p1一样是 const int*。
cout << "hello world" << endl;
}
对变量们类型的查看:
这里可以验证,类型推导对指针类型cv 限定符的推导规则。
例子:
#include
using namespace std;
int main()
{
int n = 10;
int &r1 = n;
//auto推导
auto r2 = r1;
r2 = 20;
cout << "n = " << n << ", r1 = " << r1 << ", r2 = " << r2 << endl;
//decltype推导
decltype(r1)r3 = n;
r3 = 99;
cout << "n = " << n << ", r1 = " << r1 << ", r3 = " << r2 << endl;
cout << "hello world" << endl;
}
查看变量们的类型:
总结:
可以看出来auto把引用抛弃了,decltype则保留了引用。
闲谈:auto 虽然在书写格式上比 decltype 简单,但是它的推导规则复杂,有时候会改变表达式的原始类型;而 decltype 比较纯粹,它一般会坚持保留原始表达式的任何类型,让推导的结果更加原汁原味。
我很早之前就写了一篇文章详细介绍了,请点连接。
decltype的由来
既然已经有了 auto 关键字,为什么还需要 decltype 关键字呢?因为 auto 并不适用于所有的自动类型推导场景,在某些特殊情况下 auto 用起来非常不方便,甚至压根无法使用,所以 decltype 关键字也被引入到 C++11 中。
用法:
decltype(exp) varname; //不初始化
decltype(exp) varname = value; //初始化
其中,varname 表示变量名,value 表示赋给变量的值,exp 表示一个表达式。
exp 注意事项
原则上讲,exp 就是一个普通的表达式,它可以是任意复杂的形式(如函数),但是我们必须要保证 exp 的结果是有类型的,不能是 void;例如,当 exp 调用一个返回值类型为 void 的函数时,exp 的结果也是 void 类型,此时就会导致编译错误。
decltype 简单用法的例子:
#include
using namespace std;
int main()
{
int a = 0;
decltype(a) b = 1;
decltype(9.9) x = 1.1;
decltype(x + 100) y;
cout << "hello world" << endl;
}
decltype它的玩法实际上可以非常复杂。当程序员使用 decltype(exp) 获取类型时,编译器将根据以下三条规则得出结果:
exp为普通表达式的例子:
#include
using namespace std;
class Student
{
public:
static int total;
string name;
int age;
float scores;
};
int main()
{
int n = 0;
const int &r = n;
Student Stu;
decltype(n) a = n; //n为int类型,a被推导为int类型
decltype(r) b = n; //r为const int&类型,b被推导为const int&类型
decltype(Stu) stu = Stu; //Stu为Student类型,stu被推导为Student类型
decltype(Student::total) c = 0; //Student::total为Student的一个int类型变量,c被推导为int类型
//Stu.name为Student的一个string类型,所以url被推导为string类型
decltype(Stu.name) ur1 = "https://blog.csdn.net/weixin_50188452?spm=1001.2014.3001.5343";
}
查看变量们的类型:
对应推导规则 1,对于一般的表达式,decltype 的推导结果就和这个表达式的类型一致。
exp 为函数调用的例子:
#include
using namespace std;
int func1(string, double);//返回值为int 类型
int& func2(int); //返回值为int& 类型
int&& func3(void); //返回值为int&&类型
const int func4(float); //返回值为const int 类型
const int& func5(int);//返回值为const int& 类型
const int&& func6(bool); //返回值为const int&& 类型
int main()
{
int n = 10;
decltype(func1("hello", 3.14)) a; //函数func1的返回值是int类型,所以a被推导为int类型
decltype(func2(3)) b = n; //函数func2的返回值是int&类型,所以b被推导为int&类型
decltype(func3()) c = 0;//函数func3的返回值是int&&类型,所以c被推导为int&&类型
decltype(func4(9.9)) d = 100;//函数func4的返回值是const int类型,d被推导为int类型(特例)
decltype(func5(9)) e = d;//函数func5的返回值是const int&类型,所以e被推导为const int&类型
decltype(func6(true)) f = 0;//函数func6的返回值是const int&&类型,所以f被推导为const int&&类型
cout << "hello world" << endl;
}
查看变量的类型,发现了一个特例
注意:exp 中调用函数时需要带上括号和参数,但这仅仅是形式,并不会真的去执行函数代码。这里对应这规则2
exp 是左值,或者被( )包围的例子:
#include
using namespace std;
class Base
{
public:
int x;
};
int main()
{
const Base obj;
decltype(obj.x) a = 0; //不带括号,符合推导规则1,所以推导为int
decltype((obj.x)) b = a;//带括号,符合推导规则3,所以推导为const int&
int n = 5, m = 10;
decltype(n + m) c = 0; //n+m得到右值,符合规则1,所以推导为int
decltype(n = n + m) d = c; //n = n+m得到左值,符合推导规则3,所以推导为int&
cout << "hello world" << endl;
}
补充左值和右值的理解:
左值是指那些在表达式执行结束后依然存在的数据,也就是持久性的数据;右值是指那些在表达式执行结束后不再存在的数据,也就是临时性的数据。有一种很简单的方法来区分左值和右值,对表达式取地址,如果编译器不报错就为左值,否则为右值。
应用于模板类型编程中的例子:
#include
#include
using namespace std;
template <typename T>
class Base
{
public:
void func(T& container)
{
m_it = container.begin();
}
private:
//typename T::iterator m_it; //一起处理会报错
//typename T::const_iterator m_it; //在之前的 C++98/03 版本下只能想办法把 const 类型的容器用模板特化单独处理
decltype(T().begin()) m_it; //C++11的做法,一起处理
};
int main()
{
const vector<int> v;
Base<const vector<int>> obj;
obj.func(v);
cout << "hello world" << endl;
system("pause");
return 0;
}
红色
红色