C++ templateの中でSTLを使っていたらよくわからない問題がおきた

自分のスキルが足りないのだろう。次のようなコード:

template<class T, class U>
class hoge{
  public:
    map<T, U> m;

    void hogegeho(){
      map<T, U>::iterator itm = m.begin();
      while( itm not_eq m.end() ){
        itm++;
      }
   };
};

がコンパイルできなくてしばらくハマってしまった。どうも

map::iterator

のところで引っかかっているらしい(itmが宣言できないらしい)。こんな難しい問題でもGoogle大先生は瞬時に応えてくださる。

つまり、解決策としては

typename map::iterator

と宣言すればよいことが分かった。でも何でこうなるのかが全然分からない…と思ったら、今度は紙メディアが答えを出してくださる。C++ランゲージクイックリファレンスによれば(pp243)、

テンプレートの定義を解析する際、コンパイラはどの名前が型で、どれがオブジェクトまたは関数なのかを知っていなければならない。非修飾名は通常の名前解決の規則に従って解決される。

以下略。全然わかんねえ。その後にサンプルコードがついていたので、それを見ながら考えたところ、テンプレートを作るときに、型Tのiteratorといわれたって、それが型なのかクラスなのか関数なのか分からないので(同じ::という記号でくっついているため)、それをコンパイラに教えてやらないと正しい処理ができない、ということらしい。その後ろについていたサンプルコードの要点を写しておく:

template< typename C >
void hoge( C& c ){
  for(typename C::iterator it = c.begin(); ....)
   c.erase(*it++);
}

こうやって知ってみれば不思議でも何でもないことだけど、暗中模索で調べてるときは「C++の仕様は本当に謎だなあ」と軽くキレかけてた。そんくらい推論できそうといえばできそうだけど。

C++ランゲージクイックリファレンス

C++ランゲージクイックリファレンス

というか、型だろうがクラスだろうが関数だろうが、名前に唯一性は保証されている(同じ名前を入れるとambiguous nameとかって意エラーをいつも吐く)はずなので、型推論のようなものを入れても不思議ではないはず。はず。

ま、これでプログラムが動く見通しがついたので、安心して今日は帰ることにします。