継承されたクラスが異なる戻り値の型(戻り値としてテンプレートを使用しない)で仮想関数を実装することは可能ですか?
回答:
場合によっては、はい、戻り値の型が元の戻り値の型と共変である限り、派生クラスが異なる戻り値の型を使用して仮想関数をオーバーライドすることが合法です。たとえば、次のことを考慮してください。
class Base {
public:
virtual ~Base() {}
virtual Base* clone() const = 0;
};
class Derived: public Base {
public:
virtual Derived* clone() const {
return new Derived(*this);
}
};
ここでBase
は、clone
を返すと呼ばれる純粋仮想関数を定義しますBase *
。派生実装では、この仮想関数は戻り値の型を使用してオーバーライドされDerived *
ます。戻り値の型はベースと同じではありませんが、いつでも書くことができるので、これは完全に安全です。
Base* ptr = /* ... */
Base* clone = ptr->clone();
toclone()
を呼び出すと、常にBase
オブジェクトへのポインタが返されますDerived*
。これは、を返しても、このポインタは暗黙的にaに変換可能でBase*
あり、操作は明確に定義されているためです。
より一般的には、関数の戻り値の型がそのシグネチャの一部と見なされることはありません。戻り値の型が共変である限り、任意の戻り値の型でメンバー関数をオーバーライドできます。
Base*
してlong
とDerived*
してint
(あるいは他の方法で回避、関係ありません)。それは動作しません。
はい。戻り値の型は、共変である限り、異なっていてもかまいません。C ++標準では、次のように説明されています(§10.3/ 5)。
オーバーライドする関数の戻り値の型は、オーバーライドされる関数の戻り値の型と同じであるか、関数のクラスと共変である必要があります。関数が関数を
D::f
オーバーライドするB::f
場合、次の基準を満たしていれば、関数の戻り値の型は共変です。
- どちらもクラスへのポインタまたはクラスへの参照です98)
- の戻り値の型の
B::f
クラスは、D::f
またはの戻り値の型のクラスと同じクラスであり、の戻り値の型のクラスの明確な直接または間接の基本クラスでありD::f
、でアクセス可能です。D
- ポインタまたは参照の両方が同じcv-qualificationを持ち、の戻り値の型のクラスタイプは、の戻り値の型のクラスタイプ
D::f
と同じcv-qualificationまたはそれ以下のcv-qualificationを持ちB::f
ます。
脚注98は、「クラスへのマルチレベルポインタまたはクラスへのマルチレベルポインタへの参照は許可されていない」と指摘しています。
要するに、D
がのサブタイプである場合B
、の関数D
の戻り値の型は、の関数の戻り値の型のサブタイプである必要がありますB
。戻り値の型は、自身が基づいているとき、最も一般的な例があるD
とB
、彼らがする必要はありません。これを考えてみましょう。ここでは、2つの別々のタイプ階層があります。
struct Base { /* ... */ };
struct Derived: public Base { /* ... */ };
struct B {
virtual Base* func() { return new Base; }
virtual ~B() { }
};
struct D: public B {
Derived* func() { return new Derived; }
};
int main() {
B* b = new D;
Base* base = b->func();
delete base;
delete b;
}
これが機能する理由は、の呼び出し元がポインタをfunc
期待しているためBase
です。どんなBase
ポインタでもかまいません。したがって、D::func
常にDerived
ポインタを返すことが約束されている場合、任意のDerived
ポインタを暗黙的にBase
ポインタに変換できるため、祖先クラスによって設定されたコントラクトを常に満たします。したがって、発信者は常に期待どおりの結果を得ることができます。
一部の言語では、戻り値の型を変更できることに加えて、オーバーライドする関数のパラメーター型も変更できます。彼らがそれをするとき、彼らは通常反変である必要があります。あれば、されてB::f
受け入れDerived*
、その後、D::f
受け入れることを許可されますBase*
。子孫は、受け入れるものを緩くし、返すものを厳しくすることができます。C ++では、パラメーター型の反変性は許可されていません。パラメータタイプを変更すると、C ++はそれをまったく新しい関数と見なすため、オーバーロードと非表示になり始めます。このトピックの詳細については、ウィキペディアの共変性と反変性(コンピューターサイエンス)を参照してください。
仮想関数の派生クラスの実装は、共変リターンタイプを持つことができます。