<<通用的输入输出---------cout的扩展>>这篇文章引出了一个问题:
一个库函数需要用到一个cout<<中的"<<"操作符,例如输出类型为vector<string>的数据,但是标准库没有实现,我们可以尝试自己重载。但是如何让库函数找到我们重载的操作符呢?
这是个问题,在C++中,我们知道:一切名字在使用之前必须先声明之。而include是简单抄写代码,也就是说我们重载的函数会出现在库函数之后,这按常理是无法实现我们陈述的问题。当然我们可以简单的在include之前给个前向声明,这种风格不是太好,我们可以把这个声明写入一个头文件中,让后让这个头文件最先被包含。
这些方案都是可行的,但是奇怪就在于标题提到的文章中出现了一个奇怪的问题,只需要把该重载函数置于namespace
std中即可,这无法解释,经过再三测试,其他函数是没有这种特性的。
经过这些天的测试发现其实其他函数同样具有该特性,对该特性的解释就是,C++中名字查找时,先查找该名字最小的那个块作用域,若找到了不再继续;若没有找到则继续查找相关的命名空间,找到了相关名字则停止查找。即使函数参数不是很匹配。也即重载机制只会在同一个命名空间中起作用,不同命名空间,及全局作用域之间不可以。
上述bug在于,头文件iterator在命名空间中,当查找"<<"时,我们只会在std中查找,且在std中已经找到了,所以停止查找,但参数不匹配所以报错。要想自己扩展的函数被库函数引用,应当将其置于std中。
贴一个小测试代码:
#include<iostream> #include<string> using namespace std; namespace std{ void Show() { } template<class T> void CallShow() { //Show(); //Show(std::string("empty")); } template<class T> void CallShow(T s) { //show(); Show(s); //Show(std::string("empty")); } template<class T> class display{ public: void CallShow(T s) { Show(s); } }; } namespace std{ void Show(int a) { std::cout<<"Empty Show!"<<std::endl; } void Show(std::string s) { std::cout<<"s = "<<s<<std::endl; } } int main() { CallShow<int>(); std::string str = std::string("song"); CallShow(str); display<std::string> d; d.CallShow(str); //Show(str); } 成功,且调用正确的函数。 |
#include<iostream> #include<string> using namespace std; namespace std{ void Show() { } template<class T> void CallShow() { //Show(); //Show(std::string("empty")); } template<class T> void CallShow(T s) { //show(); Show(s); //Show(std::string("empty")); } template<class T> class display{ public: void CallShow(T s) { Show(s); } }; } void Show(int a) { std::cout<<"Empty Show!"<<std::endl; } void Show(std::string s) { std::cout<<"s = "<<s<<std::endl; } int main() { CallShow<int>(); std::string str = std::string("song"); CallShow(str); display<std::string> d; d.CallShow(str); //Show(str); } 该程序编译不通过。 |
此程序其实还涉及到模板类实例化与名字使用前必须声明的语言特性!
此文仅引出问题,待以后解决!