[C++] テンプレートでの型定義の扱い (typedefとtypenameキーワード)

std::mapのラッパというか、コンテナアダプタをつくっていたのですが、
テンプレートのtypedefで詰まったので書いておきます。

エラトステネスのふるい

サンプルは素数のチェック用にstd::mapのアダプタを作ってみました。
template class Marker<T_Element>は、
mark, eraseで要素に対してフラグを立て、foundでフラグを参照します。
(ほとんどメンバ変数のm_mapに委譲しています。)

(まあ、本当はトラバース時にオブジェクト参照に対して
visitフラグを立てるためのコンテナアダプタでしたが、
サンプルではunsignedを要素にしました)

#include <map>
#include <iostream>
/* container adaptor for map<T,bool>.*/
template<typename T_Element>
class Marker
{
public:
    typedef T_Element element;
    typedef std::map<T_Element, bool> map;
    typedef map::iterator iterator;
private:
    Marker::map m_map;
public:
    Marker::iterator begin()  { return m_map.begin(); }
    Marker::iterator end()  { return m_map.end(); }
    bool found(Marker::element val)const
    {  return m_map.find(val) != m_map.end();  }
    void mark(Marker::element val)
    {  m_map[val] = true;  }
    void erase(Marker::element val)
    {
        Marker::iterator i = m_map.find(val);
        if (i != m_map.end()){
            m_map.erase(i);
        }
    }
};
using namespace std;
int main()
{
    Marker<unsigned> sieve;
    unsigned n=2;
    const unsigned max=100;
    
    for (unsigned i=2; i<max; ++i) {
        sieve.mark(i);
    }
    for (unsigned i=2; i*i<max; ++i) {
        if (sieve.found(i)) {
            for (unsigned j=i*2; j<max; j+=i) {
                sieve.erase(j);
            }
        }
    }
    for (Marker<unsigned>::iterator i=sieve.begin(); i!=sieve.end();++i) {
        std::cout << i->first << " ";
    }
    
    return 0;
}



ところが、g++でコンパイルしたところ、
typedef std::map<T_Element, bool> map;
typedef map::iterator iterator;
    
type ‘std::map<T_Element, bool, std::less<_Key>,
         std::allocator<std::pair<const T_Element, bool> > >’
is not derived from type ‘Marker<T_Element>’
(std::map<T_Element>は、Marker<T_Element>から派生してないよ)
というエラーをいただきました。
う~ん。。。

typename

結果的には、これは、
typedef typename map::iterator iterator 
と、キーワードtypenameを使うことでOKです。

私としてはMarker<T_Element>::map はstd::map<T_Element>だから、
内部に型iteratorをもつはずと暗黙のうちに仮定しているのですが、
コンパイラ的には、すべてのT_Elementについて
std::map<T_Element>がiteratorを「型として」持つとは言い切れないとみているのでしょうか。
(あるいはそのチェックが面倒)
そこで、typenameで、「シンボルmap::iteratorは変数ではなく型ですよ」と明示する必要があるようです。

ちなみに、g++でうまくいかなかったらBCCでエラーを再現しているのですが、
こっちではすんなり通りました(警告すらでない)。
生成される実行形式だけでなく、言語仕様(の再現度)もコンパイラによって異なるのですね。
C++って面白いというか奥深いというか……。

出力結果:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97