今天在《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 +!

 

 

原创粉丝点击