読者です 読者をやめる 読者になる 読者になる

template引数にコンテナを指定し、関数内でiterateする方法

C++

自分が修士の学生でC++を書いていたとき、どうにも分からなかったことがある。それは、template関数のtemplate引数にstd::vectorなどのコンテナを指定し、関数内でそのコンテナに対してiteratorを回す方法である。当時の自分は以下のようなコードを書いており、コンパイルエラーから抜けだせなくなった。

template <class X>
void iterateTest(const X& var)
{
  // Xはstd::vectorなどのコンテナであり、以下でそのconst_iteratorにアクセスしたい
  for(X::const_iterator itr = std::begin(var);
    itr != std::end(var); itr++)
  {
    std::cout << *itr << std::endl;
  }
}

これがコンパイルエラーになる理由が当時全く分からず、最終的に諦めてしまったのであった。

しかし、最近これに対する解答を見つけた。それが以下のコードである。

template <class X>
void iterateTest(const X& var)
{
  // X::const_iteratorの前にtypenameを付け、これが型名であることを明示
  for(typename X::const_iterator itr = std::begin(var);
    itr != std::end(var); itr++)
  {
    std::cout << *itr << std::endl;
  }
}

つまり、型名の前にtypenameをつければよかったのである。こうしなければならない理由は、X::const_iteratorとだけ書くと、これがXクラスのconst_iteratorという名前のstatic変数とも解釈できるなど、Xの正体がこの時点で不明であるがために、解釈が一意に定まらないためである。

さて、これでようやく長年の疑問が解決したわけだが、実はもっと簡単に書ける方法があることを発見した。それはrange based forを使う方法である。コードを以下に示す。

template <class X>
void iterateTestNew(const X& var)
{
  // range based forを使えばよりスッキリ書ける
  for(const auto& el : var)
  {
    std::cout << el << std::endl;
  }
}

以前の記事でも取り上げたrange based forにまさかこんな使い道があったとは・・・C++はまだまだ知らないことが多く、いろんな場面で損をしていると思う。今後も着実に勉強を進め、効率のよいコードを書けるようになりたい。