多重継承は、疑わしいあいまいな仮想関数の過負荷につながります


8

この例では、クラスFooBarはライブラリから提供されています。私のクラスBazは両方から継承します。

struct Foo
{
    void do_stuff (int, int);
};

struct Bar
{
    virtual void do_stuff (float) = 0;
};

struct Baz : public Foo, public Bar
{
    void func ()
    {
        do_stuff (1.1f); // ERROR HERE
    }
};

struct BazImpl : public Baz
{
    void do_stuff (float) override {};
};

int main ()
{
    BazImpl () .func ();
}

reference to ‘do_stuff’ is ambiguous2つの関数シグネチャが完全に異なるため、私には偽のコンパイルエラーが表示されます。do_stuff非仮想の場合はBar::do_stuff、それを明確にするために呼び出すことができますが、そうすると多態性が失われ、リンカーエラーが発生します。

名前を変更せずfuncにバーチャルdo_stuffを呼び出すことはできますか?


1
呼び出す基本クラス関数を明確にする必要があります。この
AndyG

回答:


10

あなたはこれを行うことができます:

struct Baz : public Foo, public Bar
{
    using Bar::do_stuff;
    using Foo::do_stuff;
    //...
}

wandbox gcc latestでテストし、うまくコンパイルします。関数オーバーロードの場合も同じだと思います。オーバーロードすると、なしでは基本クラスの実装を使用できなくなりますusing

実際、これは仮想関数とは何の関係もありません。次の例にも同じエラーがありますGCC 9.2.0 error: reference to 'do_stuff' is ambiguous

struct Foo
{
    void do_stuff (int, int){}
};

struct Bar
{
    void do_stuff (float) {}
};

struct Baz : public Foo, public Bar
{
    void func ()
    {
        do_stuff (1.1f); // ERROR HERE
    }
};

関連する可能性のある質問


2

名前の検索とオーバーロードの解決は異なります。名前は最初にスコープ内で見つける必要があります。つまりX、名前の使用方法とは無関係に名前do_stuffが解決されるようにを見つけてからX::do_stuff、オーバーロード解決での異なる宣言を選択しX::do_stuffます。

このプロセスでは、表示されているそのようなすべてのケースA::do_stuff、などを識別しB::do_stuff、その和集合の中でオーバーロードの解決を実行することはありません。代わりに、名前に対して単一のスコープを識別する必要があります。

このコードでは:

struct Baz : public Foo, public Bar
{
    void func ()
    {
        do_stuff (1.1f); // ERROR HERE
    }
};

Baz名前が含まれていないdo_stuffため、基本クラスを検索できます。ただし、名前は2つの異なるベースで発生するため、名前の検索ではスコープを識別できません。オーバーロードの解決までは行きません。

他の回答で提案されている修正が機能するのは、名前do_stuffがのスコープに導入され、名前にBaz2つのオーバーロードが導入されるためです。したがって、名前のルックアップはそのdo_stuff意味Baz::do_stuffを決定し、次にオーバーロード解決はとして知られている2つの関数から選択しBaz::do_stuffます。


余談ですが、シャドウイングは名前検索のもう1つの結果です(それ自体はルールではありません)。名前ルックアップは内部スコープを選択するため、外部スコープのすべては一致しません。

引数に依存するルックアップが機能している場合、さらに複雑な要因が発生します。簡単にまとめると、名前のルックアップは、クラスタイプの引数を持つ関数呼び出しに対して複数回行われます-私の回答で説明されている基本的なバージョンと、各引数のタイプに対してもう一度行われます。次に、見つかったスコープの和集合がオーバーロードセットに入ります。ただし、関数には組み込み型のパラメーターしかないため、この例には当てはまりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.