質問

この質問にはすでに答えがあります:

次の例で、なぜ明示的にステートメントを使用する必要があるのですか? b->A::DoSomething() ただではなく b->DoSomething()?

コンパイラのオーバーロード解決は、私がどのメソッドについて話しているのかを判断するべきではないでしょうか?

Microsoft VS2005を使用しています。(注記:この場合、仮想を使用しても役に立ちません。)

class A
{
  public:
    int DoSomething() {return 0;};
};

class B : public A
{
  public:
    int DoSomething(int x) {return 1;};
};

int main()
{
  B* b = new B();
  b->A::DoSomething();    //Why this?
  //b->DoSomething();    //Why not this? (Gives compiler error.)
  delete b;
  return 0;
}
役に立ちましたか?

解決

2 つの「オーバーロード」は同じスコープ内にありません。デフォルトでは、コンパイラーは、一致する名前が見つかるまで、可能な限り最小の名前スコープのみを考慮します。引数のマッチングが完了しました その後. 。あなたの場合、これはコンパイラが B::DoSomething. 。次に、引数リストを照合しようとしますが、失敗します。

解決策の 1 つは、過負荷を引き下げることです。 A の中へ Bのスコープ:

class B : public A {
public:
    using A::DoSomething;
    // …
}

他のヒント

オーバーロードの解決は C++ の最も醜い部分の 1 つです

基本的に、コンパイラは B のスコープ内で「DoSomething(int)」と一致する名前を見つけ、パラメータが一致しないことを確認し、エラーで停止します。

これは、クラス B の A::DoSomething を使用することで克服できます。

class A  
{  
  public:  
    int DoSomething() {return 0;}
};  

class B : public A  
{  
  public:  
    using A::DoSomething;
    int DoSomething(int x) {return 1;} 
};   


int main(int argc, char** argv)
{
  B* b = new B();  
  // b->A::DoSomething();    // still works, but...
  b->DoSomething();    // works now too
  delete b;  
  return 0;
}

いいえ、この動作は、誤って遠くの基本クラスから継承していることがバレないようにするために存在します。

これを回避するには、B クラスに using A::DoSomething を配置して、どのメソッドを呼び出したいかをコンパイラーに伝える必要があります。

見る この記事 この動作の概要をすばやく簡単に説明します。

派生クラスにメソッドが存在すると、基本クラス内の同じ名前を持つすべてのメソッド (パラメーターに関係なく) が非表示になります。これは、次のような問題を回避するために行われます。

class A {} ;
class B :public A
{
    void DoSomething(long) {...}
}

B b;
b.DoSomething(1);     // calls B::DoSomething((long)1));

その後、誰かがクラス A を変更します。

class A
{
    void DoSomething(int ) {...}
}

今突然:

B b;
b.DoSomething(1);     // calls A::DoSomething(1);

言い換えれば、このように機能しない場合、制御していないクラス (A) の無関係な変更が、コードの動作に暗黙的に影響を与える可能性があります。

これは、名前解決の仕組みと関係があります。基本的に、最初に名前の由来となるスコープを見つけてから、そのスコープ内のその名前のすべてのオーバーロードを収集します。ただし、あなたの場合のスコープはクラスBであり、クラスBでは、B::DoSomething 隠す A::DO何か:

3.3.7 名前の非表示 [basic.scope.hiding]

...[をちょきちょきと切る]...

3メンバー関数定義では、ローカル名の宣言は、同じ名前のクラスのメンバーの宣言を隠します。見る 基本.スコープ.クラス. 。派生クラスのメンバーの宣言(クラス.派生)同じ名前の基本クラスのメンバーの宣言を隠します。見る クラス.メンバー.ルックアップ.

名前が隠蔽されているため、A::DoSomething はオーバーロードの解決の対象にもなりません。

派生クラスで関数を定義すると、基本クラス内のその名前を持つすべての関数が非表示になります。基本クラス関数が仮想であり、互換性のあるシグネチャがある場合、派生クラス関数も基本クラス関数をオーバーライドします。ただし、視認性には影響しません。

using 宣言を使用すると、基本クラス関数を可視にできます。

class B : public A  
{  
  public:  
    int DoSomething(int x) {return 1;};  
    using A::DoSomething;
};  

それは過負荷ではありません!それは隠れているのです!

使用する関数を継承ツリーで検索するとき、C++ は引数なしで名前を使用し、定義が見つかったら停止し、引数を調べます。与えられた例では、クラス B で停止します。目的のことを実行できるようにするには、クラス B を次のように定義する必要があります。

class B : public A  
{  
  public:
    using A::DoSomething;
    int DoSomething(int x) {return 1;};  
}; 

この関数は、サブクラス内の同じ名前の関数 (ただしシグネチャが異なる) によって隠蔽されます。using A::DoSomething(); のように、using ステートメントを使用すると、非表示を再表示できます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top