一,decltype 含义和举例
decltype有啥返回啥,auto则不一样,auto可能会舍弃一些东西。
decltype 是 C++11提出的说明符。主要作用是:返回操作数的数据类型。
decltype 是用来推导类型,decltype对于一个给定的 变量名或者表达式,能推导出类型
这和auto 有啥区别呢?
auto a =10; 我们用10这个表达式 赋值给 a,那么auto 就能推断出来 a 是int,auto本身也是auto。也就是说,auto 必须初始化 获得这个值,才能知道具体的类型。
现在我们并不希望使用一个具体的值或者表达式 来对a进行初始化,那么怎么知道a 的类型呢?
decltype 就派上用场了。
decltype 特点:
1.decltype 的自动类型推断会发生在编译器(和auto 一样)
2.decltype 不会真正计算表达式的值。
decltype 后的圆括号中是个变量
const int i =0;
const int &iy = i;
auto j1 = i; //auto 的传值方式,i 的引用属性和const属性都会被抛弃。j1 = int, auto = int
decltype(i) j2 = 15; //如果decltype 中是一个变量,则变量中的const 属性会保留, j2 = const int
decltype(iy) j3 = j2;//如果decltype中是一个变量,则变量中的const属性,引用属性,都会保留,因此 j3 = const int &
decltype 是很循规蹈矩的,有啥就返回啥。
class Teacher{
public:
int i ;
}
decltype(Teacher::i) a;//会推导出 a = int
Teacher tea;
decltype(tea) tea2;// 会推导出 tea2 = Teacher
decltype(tea.i) mv_i;//会推导出 mv_i的类型是 int
int x =1;y=2;
auto &&z = x; // z 是万能引用,那么先要看x 是啥,x 是左值,万能引用并不是一种真正的类型,万能引用要不是左值引用,要不是右值引用, 万能引用只是一个概念,因此z 是一个 左值引用 int &, auto也是一个int &
auto &&zz = 100; // z 是万能引用,那么先要看后面传进来的值是啥,是100,100是个右值,因此zz就是 右值引用 int &&, auto 是一个int。
decltype(z) && h = y; //z是一个万能引用,其本质是一个左值引用,因此 h = & && ,引用折叠发挥作用,因此h 是一个 左值引用
decltype(z) & h1 = y; //z是一个万能引用,其本质是一个左值引用,因此 h1= & & ,引用折叠发挥作用(只要有一个是左值引用,就是左值引用),因此h1 是一个 左值引用。
decltype(zz) && h2 = 100;//zz是一个万能引用,其本质是一个右值引用,因此 h2 = && &&,引用折叠发挥作用,(两个都必须是右值引用,才是右值引用),因此h2是右值引用,注意的是给如果写成 decltype(zz) && h2 = y; 就会有build error,这是因为h2是右值引用,只能绑定右值,但是y是左值。
例子:
void main() {
//decltype 知识点
//decltype 是 C++11提出的说明符。主要作用是:返回操作数的数据类型。
//1 decltype 括号里面是一个变量
int a = 10; //普通
int *pa = &a;//指针
int &yinyonga = a;//左值引用
decltype(a) b;
using boost::typeindex::type_id_with_cvr;
cout << "b = " << type_id_with_cvr<decltype(b)>().pretty_name() << endl; //结果是int
decltype(pa) b1;
cout << "b1 = " << type_id_with_cvr<decltype(b1)>().pretty_name() << endl; //结果是int *
int c = 30;
double d = 80.8;
decltype(yinyonga) b2 = c; //很显然b2是一个左值引用,左值引用必须绑定左值
cout << "b2 = " << type_id_with_cvr<decltype(b2)>().pretty_name() << endl; //结果是int &
//decltype(yinyonga) b3 = d; //build error, b3 是 int&,是需要绑定在一个int 的左值上的,d是double类型的左值,因此有build error
//cout << "b3 = " << type_id_with_cvr<decltype(b3)>().pretty_name() << endl; //结果是int &
decltype(a) *wnyy1;
cout << "wnyy1 = " << type_id_with_cvr<decltype(wnyy1)>().pretty_name() << endl;
//decltype 会将a变成 int,因此 wnyy1是int *
decltype(pa) *wnyy2;
cout << "wnyy2 = " << type_id_with_cvr<decltype(wnyy2)>().pretty_name() << endl;
//decltype 会将pa变成 int *,因此 wnyy2是int **
//decltype(yinyonga) *wnyy3;//不允许使用指向 引用的指针
int a1 = 80909;
decltype(a) &wnyy4 = a1;
cout << "wnyy4 = " << type_id_with_cvr<decltype(wnyy4)>().pretty_name() << endl;
//decltype 会将a变成 int ,因此 wnyy4是int &
decltype(pa) &wnyy5 = pa;
cout << "wnyy5 = " << type_id_with_cvr<decltype(wnyy5)>().pretty_name() << endl;
//decltype 会将pa变成 int * ,因此 wnyy5是int *&
int &yingyonga2 = a;
decltype(yinyonga) &wnyy6 = yingyonga2;
cout << "wnyy6 = " << type_id_with_cvr<decltype(wnyy6)>().pretty_name() << endl;
//decltype 会将yinyonga变成 int& ,按照引用折叠规则,因此 wnyy6是int &
int && youzhiyinyonga = 231;
decltype(youzhiyinyonga) &wnyy7 = yingyonga2;
cout << "wnyy7 = " << type_id_with_cvr<decltype(wnyy7)>().pretty_name() << endl;
//decltype 会将youzhiyinyonga变成 int&& ,按照引用折叠规则,因此 wnyy7是int &
decltype(youzhiyinyonga) &&wnyy8 = 90;
cout << "wnyy8 = " << type_id_with_cvr<decltype(wnyy8)>().pretty_name() << endl;
//decltype 会将youzhiyinyonga变成 int&& ,按照引用折叠规则,因此 wnyy8是int &&
decltype(yingyonga2) && wnyy9 = yinyonga;
cout << "wnyy9 = " << type_id_with_cvr<decltype(wnyy9)>().pretty_name() << endl;
//decltype 会将yingyonga2变成 int& ,按照引用折叠规则,因此 wnyy9是int &
}
decltype 后的圆括号中非变量(是表达式)
decltype 会返回 表达式的 结果类型。
decltype(8) kkk = 5;//decltype是计算 表达式8的结果类型,也就是 int,因此KKK是int
decltype(8.0) kkk1 = 5;//decltype是计算 表达式8.0的结果类型,也就是 float,因此KKK是float
void main() {
//decltype 后的圆括号中非变量(是表达式)
decltype(8) kkk = 5;//decltype是计算 表达式8的结果类型,也就是 int,因此KKK是int
decltype(8.0) kkk1 = 5;//decltype是计算 表达式8.0的结果类型,也就是 float,因此KKK是float
using boost::typeindex::type_id_with_cvr;
cout << "kkk = " << type_id_with_cvr<decltype(kkk)>().pretty_name() << endl; //结果是int
cout << "kkk1 = " << type_id_with_cvr<decltype(kkk1)>().pretty_name() << endl; //结果是int
int a = 10;
int *pa = &a;
int &yinyonga = a;
decltype(a + 10) kkk2 = 5;
cout << "kkk2 = " << type_id_with_cvr<decltype(kkk2)>().pretty_name() << endl; //结果是int
//kkk2是 int ,由于 a+10这个表达式的结果是int,因此kkk2是int
decltype(pa + 10) kkk3 ;
cout << "kkk3 = " << type_id_with_cvr<decltype(kkk3)>().pretty_name() << endl; //结果是int
//kkk3是 int* ,由于 pa+10这个表达式的结果是int *,因此kkk3是int *
//decltype(*pa) kkk4 = 88;//会有build error
int b = 800;
decltype(*pa) kkk5 = b;//注意这里,会有不同
cout << "kkk5 = " << type_id_with_cvr<decltype(kkk5)>().pretty_name() << endl; //结果是int
//kkk5是 int& ,
//*pa 是指针pa所指向的对象,而且能够给对象赋值,类似 *pa = 800;因此 *pa 是一个左值,
// *pa除了在定义变量的时候,其他时间都是一个表达式。
// 注意这句话:如果表达式结果能够作为赋值语句左边的值,那么decltype后返回的就是引用
//这也是为啥 kkk4 赋值=80的时候,会有build error,因为是int &,需要一个左值,而80是个右值
//这种情况要专门记一下。
//整理为:如果decltype后面是一个非变量的表达式,并且表达式能够作为等号左边内容,那么他返回的类型必定一个左值引用
decltype(yinyonga + 10) kkk6;
cout << "kkk6 = " << type_id_with_cvr<decltype(kkk6)>().pretty_name() << endl; //结果是int
//kkk6是 int ,由于 yinyonga+10这个表达式的结果是int ,因此kkk6是int
decltype(a) kkk7;
cout << "kkk7 = " << type_id_with_cvr<decltype(kkk7)>().pretty_name() << endl; //结果是int
//kkk7是 int ,由于 a是一个变量 ,a是int ,因此kkk7是int
decltype((a)) kkk8 = b;
cout << "kkk8 = " << type_id_with_cvr<decltype(kkk8)>().pretty_name() << endl; //结果是int
//kkk8是 int &,由于 (a)是一个表达式,注意这里a是变量,(a)是表达式 ,且(a) = 90,是可以赋值的,因此kkk8是int &
}
decltype后的圆括号是个函数
//decltype后的圆括号是个函数,那么decltype()括号中的类型为该函数返回类型,但是不会真正的去执行函数,它只会看函数的返回值
void main() {
//decltype后的圆括号是个函数,那么decltype()括号中的类型为该函数返回类型,但是不会真正的去执行函数,它只会看函数的返回值
decltype(func62()) a = 90;
using boost::typeindex::type_id_with_cvr;
cout << "a = " << type_id_with_cvr<decltype(a)>().pretty_name() << endl; //结果是int
// a的类型是int
decltype(func63(89,90.8)) b = 90000;
cout << "b = " << type_id_with_cvr<decltype(b)>().pretty_name() << endl; //结果是int
// b的类型是double
decltype(func63) c;
cout << "c= " << type_id_with_cvr<decltype(c)>().pretty_name() << endl; //结果是int
// c的类型是: double __cdecl(int,double) 有返回值,有参数,是一个可调用对象,注意不是 函数指针
//如果是函数指针,应该是 double (*)(int,double)
//如果是函数引用 ,应该是 double (&)(int,double)
//c的类型是可调用对象,c不是类型
//需要使用function 类模版来处理
function<decltype(func63)> cc2 = func63;
function<decltype(func63)> cc3 = func64;
//function<c> cc2 = func63;//build error,c是可调用对象,不是类型
cc2(20, 98.8);
cc3(98.9,30);
}
二 decltype 主要用途
主要是用于模版编程中。
1.在类模版中应付可变类型
//decltype 主要用途1:在类模版中 应付可变类型。
template <class T>
class Teacher60 {
public:
typename T::iterator iter;//typename在这里类似重命名,告知
void getbegin(T & tmpc) {
//...
iter = tmpc.begin();
//...
}
};
//类模版偏特化
template <class T>
class Teacher60<const T>{
public:
typename T::const_iterator iter;
// typename在这里的含义是:显式地告诉编译器,
//T::const_iterator是一个类型名。
// 然后用这 个类型 T::const_iterator 定义一个变量 iter
void getbegin(const T & tmpc) {
//...
iter = tmpc.begin();
//...
}
};
//使用decltype()方法处理,实参传递的是啥,我们就定义啥
template <class T>
class Teacher61{
public:
decltype(T().begin()) iter;// 使用decltype()指定类型,
//注意的是 T(),生成临时对象,调用临时对象的begin()函数
//但是由于被 decltype 包裹,因此不会调用构造函数,也不会调用begin()函数,说白了,decltype()括号里面是函数,则只是会拿 函数的返回值类型,不会执行函数
void getbegin(const T & tmpc) {
//...
iter = tmpc.begin();
//...
}
};
void main() {
// decltype主要用途1 ,应付可变类型,一般decltype主要用途还是应用于模版编程中
using conttype = std::vector<int>;
conttype myarr = { 10,20,30 };
Teacher60<conttype> ct;
ct.getbegin(myarr);
//如果我们将 conttype改成 const std::vector<int>;就会有build error
//原因是我们对于 const std::vector<int>,需要使用typename T::const_iterator iter 处理
using conttype1 = const std::vector<int>;
conttype myarr1 = { 10,20,30 };
Teacher60<conttype1> ct1;
ct1.getbegin(myarr1);
//那么对于这种问题,在C++98时代,需要通过 类模版特例化来实现。
//https://mp.csdn.net/mp_blog/creation/success/135431592
//在C++11时代,可以通过 decltype 来声明类型,这样就避免了使用类模版特例化处理这些问题
using conttype2 = const std::vector<int>;
conttype myarr2 = { 10,20,30 };
Teacher61<conttype2> ct2;
ct2.getbegin(myarr2);
}
2.通过变量表达式抽取变量类型
//decltype 主要用途2,通过变量表达式 抽取 变量类型。
void main() {
vector<int> ac;
ac.push_back(1);
ac.push_back(2);
vector<int>::size_type vsize = ac.size(); //size_type 无符号整数类型(通常是 std::size_t )
decltype(ac) bc;//bc 的类型是vector<int>
using boost::typeindex::type_id_with_cvr;
cout << "bc = " << type_id_with_cvr<decltype(bc)>().pretty_name() << endl; //结果是int
//bc = class std::vector<int, class std::allocator<int> >
decltype(ac)::iterator iter;
decltype(ac)::size_type vvsize = ac.size();
cout << "vvsize = " <<vvsize << endl;
}
3.auto 结合 decltype 构成返回 类型后置 语法
//3.auto 结合 decltype 构成返回 类型后置 语法
//先回顾一下之前学的 auto 返回类型后置语法
auto func70(int a, double b) ->int {
//auto 在这里表示的是 函数返回类型是写在->之后的。
return 0;
}
//使用decltype 自动推导。a+b的返回值类型是double,因此 auto 是double
auto func71(int a, double b)->decltype(a + b) {
return a * a + b;
}
//对于同名函数的处理问题
int func72(int & temp) {
cout << "func72(int & temp)" << endl;
return temp + 2;
}
double func72(double & temp) {
cout << "func72(double & temp)" << endl;
return temp + 2.80;
}
template <typename T>
auto Func73(T & temp)->decltype(func72(temp)) {
return func72(temp);
}
void main() {
int i = 90;
Func73(i);
double d = 90.8;
Func73(d);
}
4.decltype(auto)用法 C++14提出
问题:
用于函数返回类型
decltype(auto):把auto 理解成要推导的类型,推导过程我们采用decltype
//decltype(auto)一起使用
//先看问题
template <typename T>
T& func80(T& v1) {
v1 = v1 * 2;
return v1;
}
template <typename T>
auto& func81(T& v1) {
v1 = v1 * 2;
return v1;
}
template <typename T>
auto func82(T& v1) {
v1 = v1 * 3;
return v1;
}
//decltype(auto) 会让返回值是啥类型(v1是 T&),那么整个函数的返回值也就是 T&
template <typename T>
decltype(auto) func83(T& v1) {
v1 = v1 * 4;
return v1;
}
void main() {
int a = 100;
func80<int>(a) = 90000;//func80的返回值是 T&,是个左值,因此赋值,由于func80返回的就是a的引用,因此相当于给a赋值90000
cout << "a = " << a << endl;
//下来,我们将func80 返回值auto &
int b = 80;
func81<int>(b) = 190000;//func80的返回值是 T&,是个左值,因此赋值,由于func80返回的就是a的引用,因此相当于给a赋值90000
cout << "b = " << b << endl;
//下来,我们将func80 返回值auto
int c = 80;
//func82<int>(c) = 190000;//build error, 因为auto 返回的类型是int
cout << "c = " << c << endl;
int d = 180;
func83<int>(d) = 8190000;//使用 decltype(auto)做为返回值
cout << "d = " << d << endl;
}
用于变量声明中
int x = 90;
const int &y = 80;
auto z = y; //z的类型是int,因为是值传递,会丢弃 const 和引用
decltype(auto) z1 = y;//z1的类型为const int &
5.再谈 decltype ()中 是变量表达式的情况再讨论
decltype(auto) func85() {
int a = 90;
return a;
}
decltype(auto) func86() {
int aa = 90;
return (aa);
}
//坑点
void main() {
//我们观察 func85 和 func86
//由于func86的返回值 是用()小括号 包裹的,因此会被认为是一个表达式。因此func86的返回值是int&
//这就有问题了,因为aa是局部变量,出了func86的范围就失效了
}