boost::shared_container_iterator + Boost.Range

コンテナを生成して返す関数について.生成されたコンテナに対して挿入や削除を必要としない,つまり呼び出し元では生成されたオブジェクトの集合を単に読むだけ,という状況は多いと思われます.この場合呼び出し元において,生成されたコンテナそのものを見れる必要は無く,イテレータの範囲,つまりRangeさえあれば事足ります.また生成されたコンテナの生存期間(lifetime)を考慮する必要はなくすべきです.この場合boost::shared_container_iteratorを用いて以下のような設計が考えられます.

typename std::pair<
  boost::shared_container_iterator< std::vector<int> >,
  boost::shared_container_iterator< std::vector<int> >
>
generate_integers()
{
  boost::shared_ptr< std::vector<int> > p(new std::vector<int>());
  p->push_back(1);
  p->push_back(3);
  p->push_back(5);
  return boost::make_shared_container_range(p);
}

この戻り値はRangeのモデルなのでBoost.Rangeにある各種ユーティリティでコンテナとほぼ同じ利便さで扱えます.これの利点は呼び出し側がもはや参照されているコンテナの生存期間を考慮することなく,イテレータをあっちゃこっちゃにコピーして使い倒せることにあります.呼び出し側から見ると参照されているコンテナの所有権が完全に隠蔽されている状態になっているわけです.また,コンテナへの参照を渡す設計に比べて例外安全の強度が高く,コンテナへのauto_ptrを戻すタイプの設計と同強度の例外安全性を達成できます(Exceptional C++ 項目37『AUTO_PTR』の"auto_ptrと例外安全"を参照.ただ,あの問題設定は関数がそもそもアトミックじゃないという点で若干意地が悪いような・・・).
コンテナへのポインタ/参照を渡す設計と比較しての欠点は

  • 余分に一つ2つnewが必要
  • 戻り値の型が煩雑

です.前者の欠点はコンテナを扱っているのでそれほど際立って目立つ状況は多くないでしょう.後者の欠点についてはRangeのモデルとなる同機能の新しいクラスを作っちゃえば良いでしょう.あるいは待っていればそのうちBoostに入るかも.