6

次のコードをコンパイルしようとすると:

class A {
public:
    A(int v) : virt(v) { }
    int virt;
    int getVirt(void) const { return virt; }
};

class B : private virtual A {
protected:
    B(int v) : A(v) { }
    using A::getVirt;
};

class C : public B, private virtual A {
protected:
    C(int v) : A(v), B(v) { }
    using A::getVirt;
};

class D : public C {
public:
    D(void) : C(3) { }
    using C::getVirt;
};

#include <iostream>

int main(int argc, char *argv[]) {
    D d;
    std::cout << "The number is: " << d.getVirt() << std::endl;

    return 0;
}

D が A をインスタンス化していないというエラーが表示されます。あれは正しいですか?仮想ベースが階層に埋め込まれている場合、すべての派生クラスもそのベースから仮想的に派生する必要があるので、仮想ベースのパラメトリック コンストラクターを呼び出すことができますか?

ところで、G ++によって生成されるエラーは次のとおりです。

Main.cpp: In constructor ‘D::D()’:
Main.cpp:22:18: error: no matching function for call to ‘A::A()’
Main.cpp:22:18: note: candidates are:
Main.cpp:3:5: note: A::A(int)
Main.cpp:3:5: note:   candidate expects 1 argument, 0 provided
Main.cpp:1:7: note: A::A(const A&)
Main.cpp:1:7: note:   candidate expects 1 argument, 0 provided
4

2 に答える 2

6

これはアクセス制御とは何の関係もありません (少なくとも主に)。むしろ、仮想ベースがどのように機能するかを理解する必要があります。仮想ベース サブオブジェクトは、最も派生したクラスによって初期化されます。Aのコンストラクター初期化子リストで言及していないためD、デフォルトのコンストラクターが試行されますが、存在しません。

これを修正するには、次のAように適切に初期化しますD

 D() : A(3), C(3) { }

と言うとA(3)、12.6.2/2 に従って名前検索が実行されます。

mem-initializer-idでは、最初の非修飾識別子がコンストラクターのクラスのスコープで検索され、そのスコープで見つからない場合は、コンストラクターの定義を含むスコープで検索されます。

Drew Dorman が正しく指摘しているように、仮想基底クラスを呼び出して::A、必要なアクセスを取得することで、仮想基底クラスへの直接パスを強制することができます。

于 2013-03-15T20:17:08.457 に答える
1

Kerrek SB が言及しているように、 のコンストラクターで初期化する必要があります。AD

ただし、スコープ演算子を使用して、(プライベートに) 派生したコンテキストからアクセスしていないことをコンパイラに明示的に伝える必要もあります。A

class D : public C {
public:
    D(void) : ::A(3), C(3) { }
//            ^^ Access this constructor from a global context
    using C::getVirt;
};

これは、コードの場合と同様に、コンストラクターがパブリックである必要があることも意味します。

于 2013-03-15T20:48:10.267 に答える