ListをC++で

twitterで見たとあるつぶやきを基にJavaのListをC++でできないかを考えてみた。私の考えたコードは以下のとおり(間違いなので注意)

ちなみにlistではなくvectorでコードを書いている。

#include<vector>

struct Hoge{};
struct Derived : public Hoge{};
struct Derived2 : public Hoge{};

std::vector<Hoge> hv;

このhvにDerived,Derived2のオブジェクトを格納していくのだが、コンパイルが通ったとしてもDerived関係が格納されることはない。格納されるオブジェクトはコピーされたHogeオブジェクトである。

すばるさんにその部分を指摘されて、すばるさんが書いたコードをここに書こうと思います(まずかったら言ってください)。

#include <iostream>
#include <boost/noncopyable.hpp>
 
struct Hoge
  : private boost::noncopyable
{
  virtual ~Hoge() {}
 
  virtual void foo() { std::cout << "Hoge::foo\n"; }
 
};
 
struct Derived : public Hoge
{
  void foo() { std::cout << "Derived::foo\n"; }
};
 
struct Derived2 : public Hoge
{
  void foo() { std::cout << "Derived2::foo\n"; }
};
 
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
 
int main()
{
      // こうは書けない
      /*
      std::vector<Hoge> hv;
      std::vector<Derived> dv;
 
      Derived d;
      Derived2 d2;
      hv.push_back(d);
      hv.push_back(d2);
 
      dv.push_back(d);
      //dv.push_back(d2);  <- error
 
      for( std::size_t i = 0; i < hv.size(); ++i ) {
        hv[i].foo();
      }
      */
      // shared_ptr を使う
      std::vector< boost::shared_ptr<Hoge> > hv;
 
      boost::shared_ptr<Derived> d = boost::make_shared<Derived>();
      boost::shared_ptr<Derived2> d2 = boost::make_shared<Derived2>();
      hv.push_back(d);
      hv.push_back(d2);
 
      for( std::size_t i = 0; i < hv.size(); ++i ) {
        hv[i]->foo();
      }
 
      return 0;
}

インタフェースは上記のミスを防ぐ為にもコピーできないようにnoncopyableにしておけば良いようです(そこも指摘していただきました)。
確かにHogeポインタとして格納したvectorは、Hogeを継承したポインタも格納できます。そして、ポインタはアドレスを指す参照なのでその値(中身)自体はコピーされて格納されることが無いようです。最初のようなコードはコンパイルも通って予想もしないような結果を生んでしまうので気をつけたいですね。勉強になりました。
しかしながらユーザ側でこういう機能を実装できるC++はやっぱりいいですねー。