一:
对于以下代码:
/*
* 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 +!