您的位置:首页 > 运维架构

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

2014-10-29 16:43 190 查看
当我们用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 的指点,有了您的指点,我才明白了这个问题,谢谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: