スマートポインタ(shared_ptr)に対する共変(covariant)戻り型のメモ
頭出し
共変(covariant)戻り値について。
これを知らないと、たとえばオブジェクトのコピーを作成するcloneメソッドは以下のように書いてしまうわけですが、
これやっちゃうと、clone後、処理によってはいちいち基底型をdynamic_castして、NULLチェックして、それから処理・・・となるわけです。
#include<iostream> #include<boost/shared_ptr.hpp> class A { public : virtual A* clone(){return new A(*this);} }; class B : public A { public : virtual A* clone(){return new B(*this);} }; int main() { B *b1 = new B(); A *a = b1->clone(); B *b2 = dynamic_cast<B*>(a); if(b2){ std::cout << "some process" << std::endl; } delete b1, b2; return 0; }
しかし、ここでBクラスの戻り値はBクラスのポインタ(Not Aクラス)としても良く、これが共変戻り値ということなわけでして、
こうすると上でやってたようなめんどくさいdynamic_castまわりが不必要になって簡潔になってよい!ということです。
#include<iostream> #include<boost/shared_ptr.hpp> class A { public : virtual A* clone(){return new A(*this);} }; class B : public A { public : virtual B* clone(){return new B(*this);} }; int main() { B *b1 = new B(); B *b2 = b1->clone(); std::cout << "some process" << std::endl; delete b1, b2; return 0; }
本題
で、これをshard_ptrにも拡張して書きたいなと思った時、以下のように単純にshared_ptr被せるだけだと
#include<iostream> #include<boost/shared_ptr.hpp> class A { public : virtual boost::shared_ptr<A> clone(){return boost::shared_ptr<A>(new A(*this));} }; class B : public A { public : virtual boost::shared_ptr<B> clone(){return boost::shared_ptr<B>(new B(*this));} }; int main() { boost::shared_ptr<B> b1(new B()); boost::shared_ptr<B> b2(b1->clone()); std::cout << "some process" << std::endl; return 0; }
error C2555: 'B::clone': オーバーライドする仮想関数の戻り値の型が異なり、'A::clone' の covariant ではありません。 'A::clone' の宣言を確認してください。
とコンパイラさんに怒られる。
こういうときは一旦内部用の関数をかませてから書くとよいということです。ちゃんと多態性も効くし。
#include<iostream> #include<boost/shared_ptr.hpp> class A { public: boost::shared_ptr<A> clone(){return boost::shared_ptr<A>(cloneInner());} private: virtual A* cloneInner(){return new A(*this);} }; class B : public A { public: boost::shared_ptr<B> clone(){return boost::shared_ptr<B>(cloneInner());} private: virtual B* cloneInner(){return new B(*this);} }; int main() { boost::shared_ptr<B> b1(new B()); boost::shared_ptr<B> b2(b1->clone()); std::cout << "some process" << std::endl; return 0; }