110 C++ decltype含义,decltype 主要用途

一,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; //结果是intdecltype(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是intdecltype(8.0) kkk1 = 5;//decltype是计算 表达式8.0的结果类型,也就是 float,因此KKK是floatusing boost::typeindex::type_id_with_cvr;cout << "kkk = " << type_id_with_cvr<decltype(kkk)>().pretty_name() << endl; //结果是intcout << "kkk1 = " << type_id_with_cvr<decltype(kkk1)>().pretty_name() << endl; //结果是intint 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是intdecltype(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 errorint 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的类型是intdecltype(func63(89,90.8)) b = 90000;cout << "b = " << type_id_with_cvr<decltype(b)>().pretty_name() << endl; //结果是int// b的类型是doubledecltype(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赋值90000cout << "a = " << a << endl;//下来,我们将func80 返回值auto &int b = 80;func81<int>(b) = 190000;//func80的返回值是 T&,是个左值,因此赋值,由于func80返回的就是a的引用,因此相当于给a赋值90000cout << "b = " << b << endl;//下来,我们将func80 返回值auto int c = 80;//func82<int>(c) = 190000;//build error, 因为auto 返回的类型是intcout << "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的范围就失效了}

三 总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/686669.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言:螺旋阵

右螺旋阵 输入格式 一行&#xff0c;输入两个正整数n和m&#xff08;2≤n,m≤30&#xff09;&#xff0c;分别表示行和列数 输出格式 输出对应的右螺旋矩阵 一个4*4的右螺旋矩阵是下面这个样子的&#xff1a; 1 2 3 4 12 13 14 5 11 16 15 6 10 9 &#xff18; 7 void …

【深度优先】【广度优先】Leetcode 104 二叉树的最大深度 Leetcode 110 二叉树的最小深度

【深度优先】【广度优先】Leetcode 104 二叉树的最大深度 Leetcode 104 二叉树的最大深度解法1 深度优先 递归法 后序&#xff1a;左右中解法2 广度优先&#xff1a;层序遍历 Leetcode 110 二叉树的最小深度解法1 深度优先&#xff1a;递归 后序遍历 左右中解法2 广度优先&…

C++模板详解 —— 函数模板与类模板

C模板详解 泛型编程函数模板函数模板的概念函数模板的原理 函数模板的实例化函数模板的匹配原则类模板类模板的定义格式类模板的实例化 泛型编程 如果让你编写一个函数&#xff0c;用于两个数的交换。在C语言中&#xff0c;我们会用如下方法&#xff1a; void Swapi(int* p1,…

在PyTorch中,如何查看深度学习模型的每一层结构?

这里写目录标题 1. 使用print(model)2. 使用torchsummary库3.其余方法&#xff08;可以参考&#xff09; 在PyTorch中&#xff0c;如果想查看深度学习模型的每一层结构&#xff0c;可以使用print(model)或者model.summary()&#xff08;如果你使用的是torchsummary库&#xff0…

5.11 BCC工具之statsnoop.py简介

一,工具简介 从工具的名字也能知道,statsnoop工具用于跟踪stat()系统调用。 那么stat()系统调用是做什么的呢? stat()是很常见的一种系统调用。用于获取文件或文件系统的状态信息。通过调用 stat(),你可以获取到关于指定文件或目录的详细信息,如文件大小、文件类型、文…

2024.2.17每日一题

LeetCode N 叉树的层序遍历 429. N 叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定一个 N 叉树&#xff0c;返回其节点值的层序遍历。&#xff08;即从左到右&#xff0c;逐层遍历&#xff09;。 树的序列化输入是用层序遍历&#xff0c;每组子节点…

机试复习-4

1.string类 string类型和数值的转换 ※数值→字符串 to_string函数 //具体做法 int i1234; string gto_string(i);//这样就转成字符串1234了 //下面就是字符串转为数字&#xff0c;类似下面还有stof,stoi,stod string d "1289347647"; int j stoi(d); cout <…

正则表达式速查表

匹配字符 [abc]匹配a,b,c中的任意一个字符[^abc]不匹配a,b,c中的所有字符&#xff0c;“^”只有作为第一个字符出现才有效[a-g]匹配a-g范围内的任意一个字符[^a-g]不匹配a-g中的所有字符[H-N]匹配H-N范围内的任意一个字符[0-9]匹配0-9范围内的任意一个字符[a-gH-N]匹配a-g,H-N…

线索化二叉树(先序,中序,后序)+线索化二叉树的遍历【java详解】

目录 线索化二叉树的基本介绍&#xff1a; 举个栗子&#xff1a; 二叉树的中序线索化&#xff1a; 创建HeroNode类&#xff0c;表示节点信息&#xff1a; 编写中序线索化方法代码&#xff1a; 中序线索化遍历代码&#xff1a; 测试代码&#xff1a; 测试结果&#xff1a…

OpenHarmony系统解决方案 - 配置屏幕方向导致开机动画和Launcher显示异常

问题环境 系统版本&#xff1a;OpenHarmony-3.2-Release 问题现象 配置设备默认方向&#xff0c;例如修改为横屏显示&#xff0c;修改文件display_manager_config.xml的buildInDefaultOrientation参数值为2(Orientation::HORIZONTAL)。 源码中文件位于foundation/window/win…

在 Geoserver 中添加自定义的室内坐标系

要在 Geoserver 中添加自定义的室内坐标系&#xff0c;您需要在数据目录中的 user_projections 文件夹下创建或编辑一个 epsg.properties 文件&#xff0c;然后在文件末尾添加您的坐标系的定义&#xff0c;使用 WKT&#xff08;Well-Known Text&#xff09;格式。您还需要为您的…

WordPress站点成功升级后的介绍页地址是什么?

我们一般在WordPress站点后台 >> 仪表盘 >> 更新中成功升级WordPress的话&#xff0c;最后打开的就是升级之后的版本介绍页。比如boke112百科前两天升级到WordPress 6.4.2后显示的介绍页如下图所示&#xff1a; 该介绍除了介绍当前版本修复了多少个问题及修补了多少…

ABC341 A-G

Toyota Programming Contest 2024#2&#xff08;AtCoder Beginner Contest 341&#xff09; - AtCoder B读不懂题卡了&#xff0c;F读假题卡了&#xff0c;开题开慢了rank了 A - Print 341 题意&#xff1a; 打印一串交替出现的包含N个0&#xff0c;N1个1的01串 代码&…

2024免费人像摄影后期处理工具Portraiture4.1

Portraiture作为一款智能磨皮插件&#xff0c;确实为Photoshop和Lightroom用户带来了极大的便利。通过其先进的人工智能算法&#xff0c;它能够自动识别并处理照片中的人物皮肤、头发和眉毛等部位&#xff0c;实现一键式的磨皮美化效果&#xff0c;极大地简化了后期处理的过程。…

Switch开关(antd-design组件库)简单使用

1.Switch开关 开关选择器。 2.何时使用 需要表示开关状态/两种状态之间的切换时&#xff1b; 和 checkbox 的区别是&#xff0c;切换 switch 会直接触发状态改变&#xff0c;而 checkbox 一般用于状态标记&#xff0c;需要和提交操作配合。 组件代码来自&#xff1a; 开关 Swit…

【leetcode题解C++】51.N皇后 and 76.最小覆盖子串

51. N皇后 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方…

用Python和OpenCV搭建自己的一维码和QRCode扫描仪(步骤 + 源码)

导 读 本文主要介绍使用Python和OpenCV搭建自己的一维码和QRCode扫描仪&#xff08;步骤 源码&#xff09;。 项目简介 本文我们将创建一个程序来扫描图像中的二维码和条形码。对于这个程序&#xff0c;我们需要三个包&#xff0c;分别是OpenCV、NumPy和pyzbar。大多数 Pyth…

python中线程/线程池,进程/进程池的创建

创建子线程 # 创建子线程t1 threading.Thread(targetjob,args(1,))# 执行子线程t1.start()# 等待子线程执行print("waiting threading")t1.join()print("threading done")创建子进程 # 创建子进程p1 multiprocessing.Process(targetjob,args(1,),name&qu…

linux kernel 内存踩踏之KASAN_HW_TAGS(MTE)(三)

一、背景 linux kernel 内存踩踏之KASAN&#xff08;一&#xff09;_kasan版本跟hasan版本区别-CSDN博客 linux kernel 内存踩踏之KASAN_SW_TAGS&#xff08;二&#xff09;-CSDN博客 最后来介绍一下KASAN_HW_TAGS&#xff0c;ARM64上就是MTE&#xff0c;这个特性在ARMv8.5支…

贝叶斯网络、概率图模型、全局语义、因果链、朴素贝叶斯模型、枚举推理、变量消元

文章目录 频率学派 vs. 贝叶斯学派贝叶斯学派Probability(概率):独立性/条件独立性:Probability Theory(概率论):Graphical models (概率图模型)什么是图模型(Graphical Models)图是什么计算机科学中的图模型:为什么图模型有用?图模型:统一框架图模型在机器学习中的…