今天在《Exceptional C++》上看到的两个有意思的东西
来源:互联网 发布:猎户座飞船知乎 编辑:程序博客网 时间:2024/06/09 17:45
一:
对于以下代码:
/* * Int.h*/#include <ostream>struct Int{ std::ostream & print(std::ostream & os) const; /* other codes */};std::ostream & operator << (std::ostream & os, const Int & i){ return(i.print(os));}
为了将编译期依存性减至最小,我们一般会用一个ostream的前置声明,来取代<ostream>头文件的包含:
/* * Int.h*/class ostream;struct Int{ std::ostream & print(std::ostream & os) const; /* other codes */};std::ostream & operator << (std::ostream & os, const Int & i){ return(i.print(os));}
也许在过去是可以的(当时ostream是个类,也不在namespace std中),然而在现今它是非法的,原因有二:
1 ostream现在是在namespace std里,程序员是不允许声明namespace std任何东西的;
2 ostream现在是一个模板的typedef,它是由basic_ostream<char> typedef而成的,不仅在任何情况下前置声明basic_ostream<char>模板都会造成混乱,而且根本不可能可靠地进行前置声明,因为库的各种实现产品可能自行其事,比如加入自己额外的模板参数(在标准要求之外的),你的代码当然不会知道这些了--这是不允许程序员自己声明namespace std里元素的主要原因之一。
标准库为我们提供了头文件<iosfwd>,它包含了所有流模板(包括basic_ostream)及其标准的typedef(包括ostream)的前置声明,因此我们所需要做的只是用它替代<ostream>头文件。
/* * Int.h*/#include <iosfwd>struct Int{ std::ostream & print(std::ostream & os) const; /* other codes */};std::ostream & operator << (std::ostream & os, const Int & i){ return(i.print(os));}
二:
Koenig查找(简化版):
如果提供一个类类型的函数参数,那么在查找正确的函数名时,编译器将考虑包含参数类型的名字空间内匹配的函数名。
接口规则:对于类X,所有函数,包括自由函数,只要符合如下条件:
1 “提及”X;
2 与X“一起提供”(在相同的头文件或在同一个namespace中)。
逻辑上都是X的一部分,因为它们构成了X接口的一部分。
下面是Myers例子:
namespace NS{ class T { };}void f(NS::T) { }int main(){ f(NS::T()); // OK return(0);}
namespace NS{ class T { }; void f(T) { } // new function}void f(NS::T) { }int main(){ f(NS::T()); // error: ambiguous return(0);}
#include <iostream>using namespace std;namespace A{ class T { }; ostream & operator << (ostream & os, const T &) { return(os << "A"); }}namespace B{ ostream & operator << (ostream & os, const A::T &) { return(os << "B"); } void g(A::T param) { cout << param; } // error: ambiguous}int main(){ return(0);}
namespace A{ class T { }; void f(const T &) { }}namespace B{ void f(const A::T &) { } void g(A::T param) { f(param); } // error: ambiguous}int main(){ return(0);}
注意!下面这个不是Myers例子:
namespace A{ class T { }; void f(const T &) { }}class B // B is not a namespace but a class now!{ void f(const A::T &) { } void g(A::T param) { f(param); } // OK};int main(){ return(0);}
这里B是一个class而不是一个namespace,没有任何二义性,当编译器发现一个名为f()的成员函数,它将不会试图使用Koenig查找规则来发现自由函数。
下面的例子可以更明确的说明这一点:
namespace A{ class T { }; void f(const T &) { }}class B // B is not a namespace but a class now!{ /* * hide other functions named 'f' * which in other domain */ void f() { } void g(A::T param) { f(param); } // no matching function};int main(){ return(0);}
再来看一个例子:
namespace A{ class T { };}void operator + (A::T, int) { }namespace B{ struct U { }; /* * hide other functions named 'operator +' * which in other domain */ void operator + (U, int) { } /* no match for 'operator +' */ void test(A::T param) { param + 1; }}int main(){ return(0);}
这种结果不是我们想要的,如果我们想让 param + 1 调用正确函数,
可以用域作用符(::)明确地指示我们要调用哪个函数:
namespace A{ class T { };}void operator + (A::T, int) { }namespace B{ struct U { }; /* * hide other functions named 'operator +' * which in other domain */ void operator + (U, int) { } /* call global 'operator +' explicitly */ void test(A::T param) { ::operator +(param, 1); } // ok}int main(){ return(0);}
或是用using声明:
namespace A{ class T { };}void operator + (A::T, int) { }namespace B{ struct U { }; /* * hide other functions named 'operator +' * which in other domain */ void operator + (U, int) { } /* using declaration of global 'operator +' */ using ::operator +; void test(A::T param) { param + 1; } // ok}int main(){ return(0);}
但是,如果我们无法用上面的方法呢?
比如:test是namespace std中的一个函数,我们就没法用上面的两种方法来调用正确的operator +!
我们还有一个办法,那就是前面讲到的“Koenig查找”!如果我们将第一个 operator + 放入namespace A中,编译器就可以用“Koenig查找”找到正确的operator +!
namespace A{ class T { }; void operator + (A::T, int) { }}namespace B{ struct U { }; /* * hide other functions named 'operator +' * which in other domain */ void operator + (U, int) { } /* * we need do nothing this time * Koenig search (ADL) will get A::operator + */ void test(A::T param) { param + 1; } // ok}int main(){ return(0);}
为了防止莫名其秒的函数隐藏给我们带来不必要的痛苦,我们应该将类的全部(包括与类类型相关的自由函数),放入相同的名字空间!
下面的代码也可以:
class T { };void operator + (T, int) { }namespace B{ struct U { }; /* * hide other functions named 'operator +' * which in other domain */ void operator + (U, int) { } /* * we need do nothing this time * Koenig search (ADL) will get A::operator + */ void test(T param) { param + 1; } // ok}int main(){ return(0);}
因为类T与我们都要的operator +定义在同一个文件中,也就是说它们其实是定义在同一个无名的名字空间中,所以,可以由“Koenig查找”找到这个operator +!
- 今天在《Exceptional C++》上看到的两个有意思的东西
- 今天看到的最有意思的东西!
- 今天上qq空间看到一个有意思的东西,类似射线的效果
- 在expert C上看到的一个好玩的东西
- 今天在其他的论坛上看到的一些c/c++得题!
- 今天在网上无意间看到的周星驰经典对白.呵呵..比较有意思
- 看到两个很棒的东西 - PostgreSQL & Doctrine
- 今天看到了一个非常有意思的签名档......
- 今天在CSDN上看到的GOOGLE编程挑战赛练习题
- [转贴]CSDN上看到的搞笑东西
- MDCC上看到的东西 for md
- c trap里看到的一些东西
- 《C专家编程》看到的一种有意思的画图方法
- 在非技术版块看到的一个东东,很有意思!
- 在CSDN上看到的一些好东西,分享一…
- 通宵看到的东西
- 一点看到的东西
- 在CSDN论坛上看到一个数据库帖子,觉得挺有意思的,特转过来跟大家分享
- Oracle 触发器
- Silverlight中设计焦点和文本框回车到下一个控件
- java版仿QQ2012源代码分享
- Android学习笔记——图形与图像处理
- 如何过滤让输入框 只输入字母和数字
- 今天在《Exceptional C++》上看到的两个有意思的东西
- QT安装
- linux 顶层Makefile执行的流程!!!(一)
- 网站HTML5视频问题汇总
- 浮点数的表示
- linux 顶层Makefile执行的流程!!!(二)
- PHP Beautifier的安装和使用
- 为什么不要在构造函数中调用虚函数
- 【阅读笔记之四】《DIRECTX.9.0.3D游戏开发编程基础》绘制流水线