6

次の C++ コードをコンパイルすると、これらのエラーが発生します。

covariant.cpp:32:22: error: invalid covariant return type for ‘virtual Q<B> C::test()’
covariant.cpp:22:22: error:   overriding ‘virtual Q<A> B::test()’

コンパイルエラーは削除されますが、行virtual Q<B> test() {}を変更したくありません。virtual Q<A> test() {}この問題を解決する別の方法はありますか?

template <class T>
class Q
{
    public:
        Q() {}
        virtual ~Q() {}
};

class A
{
    public:
        A() {}
        virtual ~A() {}    
};

class B
{
    public:
        B() {}
        virtual ~B() {}

        virtual Q<A> test() = 0;

};

class C : public B
{
    public:
        C() {}
        virtual ~C() {}

        virtual Q<B> test() {}
};
4

3 に答える 3

10

Q<B>およびQ<A>無関係なクラスです。Bあなたが を呼び出すクライアントであると想像してくださいtest()。結果がどのタイプになるかわからない場合、結果を何に割り当てますか?

Q<A>との両方Q<B>が同じクラス テンプレートのインスタンスであるという事実は、それらが 2 つの完全に無関係なクラスであり、(テンプレートの特殊化により) レイアウトが完全に異なる可能性があるという事実を変更しません。

これは、次のことと何ら変わりはありません。

struct X
{
    virtual std::string test() = 0;
};

struct Y : X
{
    virtual int test() { return 42; } // ERROR! std::string and int are
                                      // unrelated, just as Q<A> and Q<B>
};

test()へのポインターを呼び出すクライアントXは、結果が であると予想しますがstring、「おっと!」、そのポインターが指すオブジェクトの型Yは であり、 の戻り値の型はY::test()ですint。何が起こるべきですか?ランタイムクラッシュ?

Y y;
X* p = &y;
std::string s = p->test(); // D'OH!

C++ は静的に型付けされた言語です。つまり、コンパイル時に型チェックが実行されます。この場合、コンパイラからのメッセージは、派生クラスが派生元のクラスのインターフェイスに準拠していないことを通知するためにあります。

「無効な共変の戻り値の型」、特に「共変」という言葉が何を意味するのか疑問に思っているなら、それは簡単に説明できます。

を返すB仮想関数を持つ基本クラスがあるとします。foo()X*

struct B
{
    virtual X* foo();
};

そして、 を返すことによってオーバーライドDから派生したクラスがあるとします。ここで、は から派生したクラスです。Bfoo()Y*YX

struct D : B
{
    virtual Y* foo();
};

これは問題ですか?正しい答えは、次のやや適切な質問に答えることから得られます。foo()X*

そして、その質問に対する答えは明らかに「いいえ」です。Yは の派生クラスであるため、 へのポインターの代わりにXへのポインターを返すことができます。YX

D d;
B* b = &d;
X* p = b->foo(); // Returns an Y*, but that's OK, because a pointer to Y can be
                 // assigned to a pointer to X

これは、共変の戻り値の型の例です。あなたの例では、 の戻り値の型は の戻りC::test() 値の型に関して共変ではありませんB::test()

于 2013-06-22T19:20:30.087 に答える
1

シグネチャを持つ関数は、B::test(void)タイプ のオブジェクトを返しますが、Q<A>(C::test(void)これは同じシグネチャであるため、関数を上書きしています) を返しますQ<B>。それは不可能だと思います。

私の知る限り、戻り値の型で関数をオーバーロードすることは不可能であり、親関数の上書きは同じ戻り値の型に固執する必要があります。

規格 §10.3/7 から

オーバーライド関数の戻り値の型は、オーバーライドされた関数の戻り値の型と同じか、関数のクラスと共変でなければなりません。関数 D::f が関数 B::f をオーバーライドする場合、次の基準を満たす場合、関数の戻り値の型は共変です。

  • 両方ともクラスへのポインター、両方ともクラスへの左辺値参照、または両方ともクラスへの右辺値参照である112
  • B::f の戻り値の型のクラスは、D::f の戻り値の型のクラスと同じクラスであるか、D:: の戻り値の型のクラスの明確でアクセス可能な直接または間接の基本クラスです。へ
  • ポインターまたは参照の両方が同じ cv 修飾を持ち、D::f の戻り値の型のクラス型の cv 修飾が、B::f の戻り値の型のクラス型と同じか、cv 修飾が少ない。
于 2013-06-22T19:19:32.473 に答える
0

それをしてはいけない。仮想関数のオーバーライドは、共変の戻り値の型などの非常に特殊なケースを除いて、関数のプロトタイプを変更できません。

仮想ベースで返された型のサブクラスを仮想オーバーライドで返す場合、共変リターンは有効です。しかし、あなたのQ<A>Q<B>は継承によって無関係です。Bが のサブクラスであるという事実は、Aここでは何の違いもありません。

于 2013-06-22T19:21:03.853 に答える