これは機能しません。
- 共変リターンが機能するために必要なポインターまたは参照を返していません。と
Foo<B>
andに関係なく、Foo<B>
継承関係はありません(そうする特殊化がない限り)。Foo
A
B
しかし、私たちはそれを回避することができます。まず、要素の追加をサポートできないという理由だけで、言語の制限に関係なく、std::vector<A*>
とstd::vector<B*>
は互いに置換できないことに注意してください。そのため、代わりになるカスタムアダプターを作成することさえできませんstd::vector<B*>
A*
std::vector<B*>
std::vector<A*>
しかし、 の読み取り専用コンテナーは、 の読み取り専用コンテナーのB*
ように見えるように適合させることができますA*
。これは多段階のプロセスです。
読み取り専用のコンテナーのようなインターフェースをエクスポートする抽象クラス テンプレートを作成する
template <class ApparentElemType>
struct readonly_vector_view_base
{
struct iter
{
virtual std::unique_ptr<iter> clone() const = 0;
virtual ApparentElemType operator*() const = 0;
virtual iter& operator++() = 0;
virtual iter& operator--() = 0;
virtual bool operator== (const iter& other) const = 0;
virtual bool operator!= (const iter& other) const = 0;
virtual ~iter(){}
};
virtual std::unique_ptr<iter> begin() = 0;
virtual std::unique_ptr<iter> end() = 0;
virtual ~readonly_vector_view_base() {}
};
イテレータ自体ではなく、イテレータへのポインタを返しますが、心配する必要はありません。このクラスは、とにかく STL のようなラッパーによってのみ使用されます。
次に、 とそのイテレータの具体的なラッパーを作成してreadonly_vector_view_base
、 へのポインタを含め、その操作を に委譲しますreadonly_vector_view_base
。
template <class ApparentElemType>
class readonly_vector_view
{
public:
readonly_vector_view(const readonly_vector_view& other) : pimpl(other.pimpl) {}
readonly_vector_view(std::shared_ptr<readonly_vector_view_base<ApparentElemType>> pimpl_) : pimpl(pimpl_) {}
typedef typename readonly_vector_view_base<ApparentElemType>::iter iter_base;
class iter
{
public:
iter(std::unique_ptr<iter_base> it_) : it(it_->clone()) {}
iter(const iter& other) : it(other.it->clone()) {}
iter& operator=(iter& other) { it = other.it->clone(); return *this; }
ApparentElemType operator*() const { return **it; }
iter& operator++() { ++*it; return *this; }
iter& operator--() { --*it; return *this; }
iter operator++(int) { iter n(*this); ++*it; return n; }
iter operator--(int) { iter n(*this); --*it; return n; }
bool operator== (const iter& other) const { return *it == *other.it; }
bool operator!= (const iter& other) const { return *it != *other.it; }
private:
std::unique_ptr<iter_base> it;
};
iter begin() { return iter(pimpl->begin()); }
iter end() { return iter(pimpl->end()); }
private:
std::shared_ptr<readonly_vector_view_base<ApparentElemType>> pimpl;
};
readonly_vector_view_base
次に、異なる型の要素のベクトルを参照するテンプレート化された実装を作成します。
template <class ElemType, class ApparentElemType>
struct readonly_vector_view_impl : readonly_vector_view_base<ApparentElemType>
{
typedef typename readonly_vector_view_base<ApparentElemType>::iter iter_base;
readonly_vector_view_impl(std::shared_ptr<std::vector<ElemType>> vec_) : vec(vec_) {}
struct iter : iter_base
{
std::unique_ptr<iter_base> clone() const { std::unique_ptr<iter_base> x(new iter(it)); return x; }
iter(typename std::vector<ElemType>::iterator it_) : it(it_) {}
ApparentElemType operator*() const { return *it; }
iter& operator++() { ++it; return *this; }
iter& operator--() { ++it; return *this; }
bool operator== (const iter_base& other) const {
const iter* real_other = dynamic_cast<const iter*>(&other);
return (real_other && it == real_other->it);
}
bool operator!= (const iter_base& other) const { return ! (*this == other); }
typename std::vector<ElemType>::iterator it;
};
std::unique_ptr<iter_base> begin() {
iter* x (new iter(vec->begin()));
std::unique_ptr<iter_base> y(x);
return y;
}
std::unique_ptr<iter_base> end() {
iter* x (new iter(vec->end()));;
std::unique_ptr<iter_base> y(x);
return y;
}
std::shared_ptr<std::vector<ElemType>> vec;
};
OK、 と のように一方が他方に変換可能な 2 つの型がある限り、 のベクトルを のベクトルであるかのようにA*
見るB*
ことができます。B*
A*
しかし、それは私たちに何をもたらしますか? はまだ!readonly_vector_view<A*>
とは無関係です。readonly_vector_view<B*>
読む...
共変の戻り値の型は実際には必要ないことがわかりました。それ以外の場合は、C++ で使用できるものに対する構文糖衣です。C++ に共変の戻り値の型がない場合、それらをシミュレートできますか? それは実際にはとても簡単です:
class Base
{
virtual Base* clone_Base() { ... actual impl ... }
Base* clone() { return clone_Base(); } // note not virtual
};
class Derived : public Base
{
virtual Derived* clone_Derived() { ... actual impl ... }
virtual Base* clone_Base() { return clone_Derived(); }
Derived* clone() { return clone_Derived(); } // note not virtual
};
実際には非常に簡単で、戻り値の型がポインターまたは参照である必要も、継承関係を持つ必要もありません。変換があれば十分です。
class Base
{
virtual shared_ptr<Base> clone_Base() { ... actual impl ... }
shared_ptr<Base> clone() { return clone_Base(); }
};
class Derived : public Base
{
virtual shared_ptr<Derived> clone_Derived() { ... actual impl ... }
virtual shared_ptr<Base> clone_Base() { return clone_Derived(); }
shared_ptr<Derived> clone() { return clone_Derived(); }
};
同様に、 を返し、A::test()
を返すように手配できます。これらの関数は仮想ではないため、戻り値の型が何らかの関係にある必要はありません。一方が他方を隠しているだけです。しかし、内部では、実装された を作成する (たとえば) を作成する仮想関数を呼び出します。これは、 の観点から実装されており、すべてが実際の共変の戻り値の型であるかのように機能します。readonly_vector_view<A*>
B::test()
readonly_vector_view<B*>
readonly_vector_view<A*>
readonly_vector_view_impl<B*, A*>
vector<B*>
struct A
{
readonly_vector_view<A*> test() { return test_A(); }
virtual readonly_vector_view<A*> test_A() = 0;
};
struct B : A
{
std::shared_ptr<std::vector<B*>> bvec;
readonly_vector_view<B*> test() { return test_B(); }
virtual readonly_vector_view<A*> test_A() {
return readonly_vector_view<A*>(std::make_shared<readonly_vector_view_impl<B*, A*>>(bvec));
}
virtual readonly_vector_view<B*> test_B() {
return readonly_vector_view<B*>(std::make_shared<readonly_vector_view_impl<B*, B*>>(bvec));
}
};
簡単です!ライブデモ努力する価値は十分にあります!