C++Exceptional名称検索、ネーミングスペース、インタフェースの原則

6260 ワード

この章の内容は、次の例から引用されます.
namespace A
{
    struct X;
    struct Y;
    void f(int);
    void g(x);
}

namespace B
{
    void f(int i)
    {
        f(i); // which f
    }
    void g(A::X x)
    {
        g(x); // which g
    }
    void h(A::Y y)
    {
        h(y); //which h
    }
}

以下注意して以上の質問に答えます:1.よく答えて、呼び出したのはBの中のfで、つまり呼び出したのは自分です.2.ここでの呼び出しには曖昧なものがある.すなわち、ここでは、呼び出しがAのgであるかBのgであるかを明確に指摘する必要がある.ここでは、Bのgを呼び出すべきではないかと疑問に思う人が多いかもしれませんが、なぜAのgと曖昧なのでしょうか.この問題を説明するために、私たちはKoenig Lookupの概念を引き出します:もしあなたが1つの関数のパラメータの1つのタイプ(ここでxはタイプA::Xを持っています)を提供するならば、コンパイラは相応のネーミング空間(ここではA)の中で一致する関数を探します.
ここでは、xのタイプA:Xを指定しているので、コンパイラはデフォルトのネーミングスペースで一致する関数を検索するほか、Aのネーミングスペースで一致する関数を検索するので、ここで曖昧になります.
では、問題はコンパイラがなぜそうするのかということです.まず、クラスの定義は、1つのクラスが1つのデータの集合と、これらのデータ上で動作する関数を記述することです.まず、次の質問に答えます.1つのクラスには何がありますか.あるいは、何のクラスとインタフェースの一部ですか.クラスのメンバー変数、メンバー関数、静的関数などは、クラスとインタフェースの一部であることは明らかです.次のコードを考慮します.
//*** Example 1(a)
class X { /*...*/ }
/*...*/
void f( const X&);

//*** Example 1 (b)
class X
{
    /*...*/
public:
    void f() const;
}

上の2つのfの違いをよく考えてみると、1つは表示されているXを除いて、1つは暗黙的にポインタを伝える以外に、この2つの関数には何の違いもありません.したがって、2番目のfがXの一部であるとすれば、1番目のfもXの一部であるべきである.
Xに属するクラスの定義を以下に示す:1.クラスX 2を用いた.クラスXと同じファイルまたは同じネーミングスペースにあります.以上の2つの条件を満たす関数であれば,クラスXの一部と見なすことができる.
コンパイラは、上記の2つの条件を満たす関数を見ると、この関数との関係を確立します.ここでは2つの状況に分けられます.1つは、この関数がクラスのメンバー関数である場合、このような関数とこの関数の関係が強くなります.もう1つのケースでは、クラスのメンバー関数ではありませんが、この関数とクラスが同じファイルまたは同じネーミングスペースにある場合、この関数とこの関数の関係は弱点になります.コンパイラはどの関数を使うかを選択すると、必ず関係の強い関数を選択します.クラスと関数の関係だからこそ,C++はKoenig Lookup法則を用いて関数をマッチングする.
次の例を見てください.
//*** Example 
namespace NS
{
    class T {}
}
void f( NS::T); //global function
int main()
{
    NS::T param;
    f(param);
}

上記の例では、グローバルなf関数が呼び出されることが明らかです.次の例を見てみましょう.
namespace NS
{
    class T{ };
    void f(T);
}

void f(NS::T);

int main()
{
    NS::T param;
    f(param);
}

今、残念ながら、ここのfには曖昧さが現れ、fはNSネーミング空間とグローバルに同時に存在するため、曖昧さが生じている.次に、次の例を示します.
namespace A
{
    class x{};
    void f(X);
}

namespace B
{
    void f(A::X);
    void g(A::X param)
    {
        f(param);
    }
}

同様に,以上のコードも曖昧になり,fはAとBのネーミング空間に同時に存在するため曖昧になる.次の例は例外です.
namespace A
{
    class X{};
    void f(X);
}

class B
{
    void f(A::X);
    void g(A::X param)
    {
        f(param);
    }
}

このコードと上のコードには何か違いがあるのではないでしょうか.違いもあるのではないでしょうか.違いは、ここのBはnamespaceではなくclassであり、fはメンバー関数として存在するが、メンバー関数の優先度はネーミングスペースよりも高いので、ここでは曖昧ではなく、B::f(A::X)が呼び出される.
また、次のような文を書くときに
std::cout << i << std::endl;

我々はoperator最後に、関数の非表示に関する知識を説明します.クラスのサブクラスに親クラスの同名関数が宣言されている場合、パラメータが異なると、子クラスの関数は親クラスの関数を非表示にし、親クラスの同名関数を呼び出すには、親クラスの関数をネーミングスペースに導入する必要があることを知っています.どうしてこんなことになったの?パラメータが異なる以上、呼び出し時に親クラスの対応する関数を正確に見つけることができるのではないでしょうか.これはC++が関数を探す際に採用するルールに関連しています.まず、名前(このときアクセス権やパラメータの問題は考慮されません)に基づいて自分のクラスを探します.見つけたら、探し続けません.次に、関数のアクセス権、パラメータが変換できるかどうかなどの問題をチェックします.この検索規則によって、以上の問題が発生します.
  • 以上述べたように、実は3つのポイントについて:Koenig Lookup法則を知っていて、この法則が適用する2つの条件を知っています:(1)関数はクラス(2)関数とクラスを使って同じファイルで提供して、あるいは同じネーミング空間の中で以上の条件を満たすだけで、c++はこの関数とクラスが一定の関係があると思って、名前が検索する時、Koenig Lookupの法則を運用することができます.
  • Koenig Lookup法則を用いて関連付けられた関数はクラスのメンバ関数よりも弱く,c++はメンバ関数
  • を優先的に選択する.
  • C++が関数を探すときの具体的なルールを理解するには、まず名前で検索します.このときはアクセス権とパラメータが一致するかどうかの問題を考慮せず、見つけてからこれらの条件
  • をチェックします.