以模板的方式重载"operator <<"需要注意的地方

2023-11-16

当我们用C++进行后台开发的时候,常常需要知道某一时刻一个容器的内容。

通常,我们的做法是利用迭代器将容器内容打印到日志文件中,然后进行观察分析。如果每次打印都去找迭代器的麻烦,显然不是我们想要的。这样,顺理成章的我们就想到了封装函数。

为了更方便的使用封装的函数,我们可以采用重载"operator <<"的方式,用流输出的方式直接打印容器内容(google的glog就支持以流的方式打印日志)。支持了流输出运算符之后,不仅打印日志方便,直接输出到cout也很方便。

但是容器有很多种,为每种容器单独定义一个函数未免显得太过麻烦,于是我们可以用模板。很自然,我们会想到以下形态:

template < typename CON >

ostream & operator << (ostream &os, const CON &con)

{

    for(CON::const_iterator cit = con.begin(); cit != con.end(); cit++)

    {

        cout << *cit << ' ';  // 编译错误 error C2593: “operator <<”不明确

    }

    return os;

}

这样看似没有任何问题了,但是后面一旦调用,就会发现上述注释行有错C2593,这是为什么呢?因为这里编译器不知道这个地方的“<< ' '”输出空格这个动作,到底是在递归调用还是在调用标准的函数定义(后面是标准定义,于<ostream>头文件的869行):template<class _Elem,class _Traits> inline basic_ostream<_Elem, _Traits>& __CLRCALL_OR_CDECL operator<<(basic_ostream<_Elem, _Traits>& _Ostr, _Elem _Ch)。

编译器不能确定当然就错了。

出错的原因是因为标准定义中,两个参数均使用了模板参数 _Elem,相当于前后强制进行了关联;而我们的定义前后两个参数并没有进行关联(ostream就是typedef basic_ostream<char, char_traits<char> > ostream),因此在我们定义的时候,系统认为这两个是相互不同的模板函数的重载,但是当我们调用“<< ' '”来输出字符的时候,就恰巧使两个模板都能使用了,而且这两个模板没法比较谁更特例化,因此编译器就无法判断使用谁了。


综上,我们就要从根本的问题来解决:两个模板是不同的重载,但是又存在交叉,我们就将两个函数做成包含关系,让系统的标准定义成为我们定义的子集(成为我们定义的特例化),于是修改我们的定义如下:

template < typename a_char, typename a_trait, typename CON > basic_ostream<a_char,a_trait>& operator << (basic_ostream<a_char,a_trait>& os, const CON & con){...}

函数体实现和上面一样,这样就能编译通过了。


不过还有个问题,对于map,其迭代器指向的是pair类型,因此还需要增加以下重载来迎合map的打印:

template < typename K, typename V >
ostream & operator << (ostream &os, const pair<K, V> &p)
{
    os << '(' << p.first << ',' << p.second << ')' << flush;
    return os;
}

这里为什么又直接用ostream了呢?因为第二参数作为pair,C++标准函数并没有对pair进行流输出的定义,因此不会出现编译器无法确定该调用谁的问题。


类似的,如果需要打印的容器中的类型不支持流输出,就再对其进行流输出定义的重载就行了。


这样一来,几乎全部容器都能很方便的利用流输出进行cout打印和日志打印了(glog)。


最后:特别感谢 ri_aje 的指点,有了您的指点,我才明白了这个问题,谢谢!


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

以模板的方式重载"operator <<"需要注意的地方 的相关文章

随机推荐

  • Shamir门限方案的秘钥分享(包括逆元求解)

    Shamir门限方案的秘钥分享 不要求支持大数 题目描述 实验目的 通过基于Shamir门限方案的密钥分割及恢复的演示 理解密钥分割的重要性 理解密钥分割的基本原理和作用 掌握基于Shamir门限方案的密钥分割软件的使用 实验原理 秘密共享
  • 截取字符串中所有的数字字符

    截取字符串中的数字 param s return public static String trimToNumber String s int n s length char a new char n int len 0 for int i
  • const定义的变量,可以作为数组[ ]里面的值吗?

    结论 在c中是不可以的 在c 中可以 证明 C 编译器把Const对象放在了符号表之中 C语言一般是放在只读数据区 为什么C 编译器这么做 我想一个原因就是减少一些存储操作次数 const c int main const int a 10
  • fastadmin 微信H5支付返回格式

    记录 使用 fastadmin 的epay插件进行调用微信H5支付时 默认情况下 返回格式化的跳转页面html代码 但前端若使用vue或uni app来编写就不适用了 直接返回支付跳转地址 addons epay library Servi
  • Ubuntu 16.04.4 LTS下安装JDK

    Ubuntu 16 04 4 LTS下安装JDK 阅读目录 写在前面 方法 测试 结束 写在前面 为什么我又装jdk 今天顺手升级了我的双系统中的Ubuntu 开始的时候用的图形化界面升级 后来你懂的 升级软件死锁了 用命令行也没有效果了
  • MySQL 索引原理

    MySQL索引深入剖析 官方定义是 索引 Index 是帮助MySQL高效获取数据的数据结构 简单来说 索引是一种数据结构 以协助快速查询 更新数据库表中数据 索引的基本原理 把创建索引列的内容进行排序 对排序的结果生成倒排表 在倒排表内容
  • 关于 ag-grid 的调研之路

    前言 因为公司业务需要 需要对 ag grid数据网格进行调研 随后就开始了漫长的探索之路 废话不多说直接撸干活 ag grid 简介 AG Grid是一个功能齐全 高度可定制的JavaScript数据网格 它提供了卓越的性能 没有第三方依
  • disruptor高性能环形队列

    简介 说到Disruptor 首先需要谈谈LMAX 它是欧洲第一家也是唯一一家采用多边交易设施Multilateral Trading Facility MTF 拥有交易所牌照和经纪商牌照的欧洲顶级金融公司 它们所构建的金融交易平台 建立在
  • An error occured, please see below or look at Nuxt.js terminal for more info. Error: EACCES: permiss

    这个问题应该就是mac会遇到 把npm run dev 前面加个sudo sudo npm run dev 就可以了
  • 《人工智能导论》 第1章 绪论&第2章 知识表示&第3章 确定性推理方法&第4章 不确定推理方法

    绪论 人工智能的基本概念 智能的概念 目前对智能还没有确切的定义 主要流派有 思维理论 智能的核心是思维 认为通过对思维规律与方法的研究可揭示智能的本质 知识阈值理论 智能取决于知识的数量及一般化程度 认为智能是在巨大的搜索空间中迅速找到满
  • 华为机试training-03

    蛇形矩阵 蛇形矩阵是由1开始的自然数依次排列成的一个矩阵上三角形 例如 当输入5时 应该输出的三角形为 1 3 6 10 15 2 5 9 14 4 8 13 7 12 11 其具体实现代码如下 include
  • mybatits的PageHelper分页工具的使用

    最近开发一个后台管理系统 有一些东西记录一下 之前都是没有这个工具之前都是传递参数到xml进行分页查询 1 首先导入一些可能导入的包
  • Java 集合深入理解 (十一) :HashMap之实现原理及hash碰撞

    文章目录 前言 哈希表原理 实现示例 HashMap实现原理 全篇注释分析 实现注意事项 默认属性分析 属性分析 构造方法分析 重要的put方法 总结 前言 哈希表 hashMap 又叫散列表 是一种非常重要的数据结构基于map接口实现 应
  • STM32 MCP2515连发 多发 MCP2515收发程序 多路CAN通信 2路CAN

    MCP2515在发送数据时 如果通信速率较低一切都正常 但是当通信速率较高 比如1M时 发送一帧数据后 MCP2515有时会出现自动重复发送多帧数据的情况 为解决这现象 请参照官方给的 MCP2515勘误手册 第5条 给出了解决CAN速率较
  • IDEA正则表达式高级替换

    1 需求 需要将如下注释转换成另外一个形式 员工姓名 private String name 员工姓名 private String name 2 在idea中输入正则表达式进行快速替换 3 具体的正则表达式如下 替换前正则 替换后正则 1
  • openGL之API学习(二零二)glsl的smooth flat

    采用flat着色时 OpenGL将使用图元中某个顶点的颜色来渲染整个图元 通常情况下会选择图元的第一个或最后一个顶点的颜色作为该图元的颜色 在使用smooth着色时 OpenGL会独立的处理图元中各个顶点的颜色 对于线段图元 线段上各点的颜
  • 电子信息工程专业毕设题目选题推荐

    文章目录 1前言 2 如何选题 3 选题方向 2 1 嵌入式开发方向 2 2 物联网方向 2 3 移动通信方向 2 4 人工智能方向 2 5 算法研究方向 2 6 移动应用开发方向 2 7 网络通信方向 3 4 学长作品展示 4 最后 1前
  • Oracle开窗分析函数,Oracle 惯用的分析开窗函数

    SELECT T EMAIL USER NAME COUNT OVER PARTITION BY T EMAIL MAIL ORDER BY T EMAIL TEL DESC C FROM T BASE EMAIL T SELECT T E
  • python startswith与endswith

    python startswith与endswith 如果你要用python匹配字符串的开头或末尾是否包含一个字符串 就可以用startswith 和endswith 比如 content ilovepython 如果字符串content以
  • 以模板的方式重载"operator <<"需要注意的地方

    当我们用C 进行后台开发的时候 常常需要知道某一时刻一个容器的内容 通常 我们的做法是利用迭代器将容器内容打印到日志文件中 然后进行观察分析 如果每次打印都去找迭代器的麻烦 显然不是我们想要的 这样 顺理成章的我们就想到了封装函数 为了更方