目录

一、透过表象,直探本质

1.decltype关键字该如何理解?

2.auto VS decltype

二、decltype的使用分析

1.“()”中放入的是变量

2.“()”中放入的是表达式

3.“()”中放入的是函数

三、decltype的方便之处(实战演示)

【程序结果】

参阅资料



一、透过表象,直探本质

1.decltype关键字该如何理解?

decltype用于推导类型是大家都知道的,可真正面对decltype的各种写法时,又容易迷失在类型判断的困境中。以下面两行代码为例,来说明问题:

decltype(10) a;            //好比书写int a;
decltype('Q') b = 'b';    //好比书写char b = 'b';

相信大家也发现了,上面两句这样写非常类似于变量的定义和初始化。那么,不论decltype后的“()”中写的多么复杂,最后decltype()返回的结果必定只是一个类型而已。理解了这条本质,下面的问题讨论起来就轻松一些啦。

2.auto VS decltype

说起类型推断,马上就会有读者说:auto不也可以进行类型推断吗? auto确实可以进行类型推断,且与decltype都是在编译期进行类型推断的,但它们两者也有着以下的区别:

  • auto定义变量时必须立即初始化,而decltype使用起来更为灵活一些,“()”中可以放很多不同的东西;
  • decltype进行类型推断时,并不会计算表达式的值;
  • const限定符、引用等属性有可能会被auto抛弃,但decltype一般则不会抛弃任何东西。

二、decltype的使用分析

1.“()”中放入的是变量

这种情形下的类型推断比较好理解,故简单举例说明存在const、引用时的情况,且使用起来感觉与auto的类型推断差不多。

const int i = 1;
const int& j = i;//decltype并不会抛弃const、引用等属性
decltype(i) m1 = 5;   //m1的类型为const int
decltype(j) m2 = m1; //m2的类型为const int&

2.“()”中放入的是表达式

int i = 0;
int *pI = &i;
int &rI = i;decltype(rI + 1) j;  //因rI + 1返回的是一个整型表达式结果, 故j的类型为int
decltype(pI) k;  //因pI是一个指针变量, 故k的类型为int*
decltype(*pI) k1; //k1的类型为int&

某些读者可能会对最后一条语句的推断结果感到意外,笔者在此想做一番细致分析:首先,*pI得到的是指针所指向的对象,且*pI能作为左值使用(赋值);其次,才涉及到decltype的一条规则,即若()中的表达式能作为左值使用,那么decltype返回的就是一个引用类型。

同时还有一种写法也有类似的效果,decltype((变量))的结果也是一个引用,具体而言给变量套上一层括号后,就成为了表达式,而该表达式能作为左值使用。

3.“()”中放入的是函数

与前两种情况相比,该情形下的用法会显得不那么容易理解。还是以实例来进行说明,帮助各位读者理解decltype与函数碰撞时所隐藏的秘密。

【测试代码】

#include<iostream>using namespace std;int add(int a)
{return a;
}int main()
{decltype(add) *T = add;     //函数指针的初始化    decltype(&add) t = add;  //同上decltype(add) &t2 = add;//函数引用的初始化//lambada表达式也是一种可调用对象auto add2 = [](int a, int b) -> int  {return a + b;};decltype(add2) t1 = add2; //将t1绑定到实际的可调用对象上cout <<"add:" << T(2) << endl;cout <<"add:" << t(3) << endl;cout <<"add:" << t2(4) << endl;cout <<"add2:" << t1(3,4) << endl;return 0;
}

【程序结果】

【结果分析】

 decltype(add) *T = add;  “()”中放入的是函数名,decltype推断后的结果为函数类型int(int),其后紧跟着*修饰,则得到了一个函数指针类型的变量T,其指向函数实体add。

decltype(&add) t = add;  “()”中放入的是函数地址,decltype推断后的结果直接就是函数指针类型int(*)(int),此时得到了一个函数指针t,其指向函数实体add。

decltype(add) &t2 = add; 与加指针标识符*的情况类似,最后获得的是函数引用类型。

decltype(add2) t1 = add2;  “()”中放入的是函数名,返回的是函数类型int(int, int),此时获得是一个具有函数类型int(int, int)的对象t1。

等等...,具有函数类型的对象?啥意思? 换句话说,此时获得的t1为一种可调用对象(函数对象或仿函数),所以在上面的演示中才利用lambda表达式生成了add2对象,以作为t1的值。

三、decltype的方便之处(实战演示)

看到这儿,可能依然有读者觉得还不过瘾,甚至还可能觉得前面演示的decltype看起来也没多大威力嘛,况且还有“坑”,用不好就完蛋。

C++新标准里的东西,让人惊叹的同时,又让人感觉到费解。虽然笔者有着经典C++的基础,但在学习C++11之后的内容时,也是同大多初学者一样喜忧参半。作为新标准内容之一的decltype,最大的妙用还是在泛型编程中来使用,其强大的类型推断功能可以很好地与模板参数的推导结合起来。

下面是一份在二叉查找树中插入结点的实际例子,演示了decltype在泛型编程中的妙用:

#include<iostream>
#include<vector>
#include<stack>using namespace std;template<typename E>
struct TreeNode  //树结点结构
{E data;TreeNode<E>* left;TreeNode<E>* right;TreeNode(E e) : data(e), left(nullptr), right(nullptr) {}
};//E有可能是自定义的类类型,故我们需要传入一个自定义比较函数
//即Comparator是针对类型E定义的
template<typename E, typename Comparator>
class BST
{
private:TreeNode<E>* root;  //BST的树根结点int size;          //BST的大小Comparator cmp;   //自定义的比较函数public:BST(Comparator cmp) : root(nullptr), size(0), cmp(cmp) {}   void add(E e)  //插入结点{  if(!root) root = new TreeNode<E>(e);else {TreeNode<E>* cur = root;while(cur) {if( cmp(e, cur->data) < 0 && !cur->left ) {cur->left = new TreeNode<E>(e);size++;return;}else if ( cmp(e, cur->data) > 0 && !cur->right ){cur->right = new TreeNode<E>(e);size++;return;}//若能走到此处证明cur->right或cur->left不为空if( cmp(e, cur->data) < 0 ) cur = cur->left;else if( cmp(e, cur->data) > 0 ) cur = cur->right;}}}vector<E> inOrder()  //中序遍历{  if(!root) return {};vector<E> res;           //保存访问结果stack<TreeNode<E>*> st; //保存树结点TreeNode<E>* cur = root;  //工作的结点指针while( cur || !st.empty() ) {//1.找到最左子结点while(cur) {st.push(cur);cur = cur->left;} //2.若到达此处时没有经过1, 则证明此时弹出的是某子树的root结点TreeNode<E>* node = st.top();st.pop();res.emplace_back(node->data);cur = node->right;}return std::move(res);}
};int main()
{auto cmp = [](const int& x, const int& y) -> int{return x - y;};BST<int, decltype(cmp)> bst(cmp); //妙用之处!bst.add(33);bst.add(22);bst.add(45);bst.add(9);bst.add(35);vector<int> vc = bst.inOrder();  //中序遍历BST,返回一个有序的序列for(auto &x : vc) {cout << x << " ";}return 0;
}

【程序结果】

参阅资料

> C++ primer 第5版

> 狄泰软件学院C++深度解析教程

> C++ 新经典—王健伟

浅析decltype一些有趣(实用)的用法相关推荐

  1. C语言中typeof作用,浅析C语言中typeof关键字用法

    浅析C语言中typeof关键字用法 前言 C语言中 typeof 关键字是用来定义变量数据类型的.在linux内核源代码中广泛使用. 下面是Linux内核源代码中一个关于typeof实例: #defi ...

  2. 分享几个有趣实用的网站

    苏生不惑第335 篇原创文章,将本公众号设为星标,第一时间看最新文章. 之前分享过这几个摸鱼网站不要太沉迷和又发现几个有意思的网站 , 今天再分享几个有趣实用的网站,另外之前分享过的所有网站我都更新到 ...

  3. 那些有趣/实用的 Chrome 扩展神器系列(六)

    苏生不惑第285 篇原创文章,将本公众号设为星标,第一时间看最新文章. 话说没有安装扩展的浏览器是没有灵魂的,之前分享过几篇关于Chrome扩展的文章(微信edge也是可以安装使用的),这里继续分享第 ...

  4. 推荐一些有趣实用的前端可视化工具库,助力可视化开发

    前言 前端可视化一直是人们常谈到的词,随着SVG,Canvas,WebGL等技术的发展,也涌现了一大批的优秀的可视化库.本文将从中选择一些有趣实用的推荐给大家,工具使用好了,开发效率自然也就提升了. ...

  5. 又发现几个有趣实用的网站

    苏生不惑第349 篇原创文章,将本公众号设为星标,第一时间看最新文章. 之前分享过的网站我都整理到博客上了https://blog-susheng.vercel.app/ ,方便搜索查看,这里继续分享 ...

  6. 那些有趣/实用的 Chrome 扩展神器系列(二)

    苏生不惑第201 篇原创文章,将本公众号设为星标,第一时间看最新文章. 之前分享过 那些有趣/实用的 Chrome 扩展神器 ,准备做成一个系列,这是第2篇,关于谷歌浏览器安装Chrome扩展参考我之 ...

  7. Redis 命令行工具有趣的罕见用法

    数据技术嘉年华等你来 精彩预告:第八届数据技术嘉年华大会将于2018年11月16日~17日在北京市富力万丽酒店盛大开启.本次大会围绕数据.智能.链接组织前沿议题,倡导以智能智慧算法应用,发掘数据价值, ...

  8. python中int函数的用法浅析_Python中int()函数的用法浅析

    int()是Python的一个内部函数 Python系统帮助里面是这么说的 >>> help(int) Help on class int in module __builtin__ ...

  9. 有趣实用的app分享,强烈推荐

    App一:夸克浏览器 夸克浏览器是一个极简主义的浏览器应用程序. 其简洁的界面.流畅的动画.极致的交互体验,让人感觉特别舒服. 将输入框放在最下方,搜索历史也显示在输入框上,方便您触摸,节省流量,内置 ...

最新文章

  1. 【hexo】搭建个人bolg日志 day1
  2. mysql行列转换例子_mysql行列转换示例
  3. 非视线成像 - 把墙角变为相机
  4. 连云港职业技术学院有计算机系吗,连云港职业技术学院电子信息工程技术专业...
  5. 北大OJ百练——3179:最长单词(C语言)
  6. boost::iota相关的测试程序
  7. 线性代数之矩阵偏导续
  8. awk -f 分隔符 命令_千面 awk
  9. 中断处理的tasklet(小任务)机制-不过如此
  10. Facebook 开源:PyTorchVideo!
  11. (转)J2ME中随机数字的生成
  12. 蓝桥杯 ALGO-95 算法训练 2的次幂表示
  13. python使用长ping命令_在Python中调用Ping命令,批量IP的方法
  14. 语音推送提醒php,实时语音后台通知消息 - 百度免费的tts实现后台实时在线语音消息提醒 – 基于ThinkPHP和Bootstrap的极速后台开发框架...
  15. 中达优控触摸屏编程视频教程_中达优控触摸屏编程软件下载
  16. C语言中的万能头文件
  17. UE4教程-搭建别墅
  18. mm,mil,inch,um的转换关系
  19. MAGENTO for XAMPP install config -搬家配置与安装配置
  20. 前端网站开发页面重定向的几种方法

热门文章

  1. RabbitMQ(四)远程连接RabbitMQ
  2. Linux笔记|Linux的敲门砖
  3. 微信为什么不支持android4,解决android4.4.4手机不能正常调起微信支付问题
  4. 安全科普:使用Cookie会导致哪些安全问题?
  5. DSP与DAC数字音频接口时钟分析
  6. ENVI下的Landsat8图像融合
  7. Stream.reduce()合并流 例BigDecimal 的add求和
  8. 台式电脑CPU散热器怎么选?
  9. 01-如何选购CPU散热器?小白装机通俗易懂的水冷/风冷CPU散热器知识
  10. [API接口设计] RESTful 规范