コンテンツにスキップ

最終更新:2026-10-12

STL typename ソースコード解析

STLの低層ソースコードには以下の数行があり、typedefとtypenameが併用されています。複雑に見えますが、いったい何を意味するのか、今日は一緒に解析してみましょう!

template<typename _Iterator>
struct iterator_traits
{
  typedef typename _Iterator::iterator_category iterator_category;
  typedef typename _Iterator::value_type        value_type;
  typedef typename _Iterator::difference_type   difference_type;
  typedef typename _Iterator::pointer           pointer;
  typedef typename _Iterator::reference         reference;
};

typenameのよくある使い方

まずtypenameのよくある使い方を学びましょう:

template <typename T>
int compare(const T &a, const T &b)
{
    return a>b?a:b;
}

上記はあくまでサンプルプログラムで、比較をきちんと行うには特殊化バージョンも考慮する必要があります。ここで、上のコードのtypenameをclassに置き換えても同じように動くと思うかもしれません。その通りです!では疑問が生じます。この2つの方法には違いがあるのでしょうか?C++ Primerを調べてみると、両者はまったく同じであることがわかります。

クラススコープ

クラスの外部からクラス内の名前にアクセスするとき、クラススコープ演算子を使えます。MyClass::nameのような呼び出しには通常3種類あります:**静的データメンバ、静的メンバ関数、ネストした型**です:

struct MyClass {
    static int A; //静态成员
    static int B(){cout<<"B()"<<endl; return 100;} //静态函数
    typedef int C;  //嵌套类型
    struct A1 { //嵌套类型
        static int s;
    };
};

呼び出すときは、直接呼び出せます:

cout<<MyClass::A<<endl;
cout<<MyClass::B()<<endl;
MyClass:C c;
...

完全な例での試行

typenameの例に戻りましょう:

template <class T>
void foo() {
    T::iterator * iter;
    // ...
}

このコードの目的は何でしょうか?多くの人の第一反応は「iterというポインタを定義し、それが指す型はクラススコープTに含まれるiteratorである」と思うでしょう。次のような、iterator型を含む構造が存在すると考えられます:

struct MyIterator {
    struct iterator {

    };
};

呼び出しは次の通りです:

foo<MyIterator>();

こうなると、iterの行のコードは明確で、MyIterator::iterator型のポインタです。私たちはそのように推測しますが、実際はどうでしょうか?

しかし、T::iteratorのような場合はどうでしょうか?Tはテンプレートの型パラメータなので、テンプレートがインスタンス化されるまでどの型なのかわかりません。ましてや内部のiteratorはなおさらです。先ほどのクラススコープの説明から、T::iteratorは実際には次の3種類のいずれにもなり得ることがわかります:

  • 静的データメンバ
  • 静的メンバ関数
  • ネストした型

前の例のContainsAType::iteratorはネストした型なので、まったく問題ありません。しかし、静的データメンバだった場合はどうなるでしょうか?もしfooテンプレート関数を次のような型でインスタンス化したら:

struct MyIterator {
    static int iterator;
};

すると、T::iterator * iter;はコンパイラによってMyIterator::iterator * iter;とインスタンス化されます。これは何でしょうか?先頭にあるのは型ではなく静的メンバ変数なので、これは乗算式になってしまいます。ただしiterはここでは定義されていないため、コンパイラはエラーを出します:

error: no type named ‘iterator’ in ‘struct MyIterator’

typename

テンプレート定義においてテンプレートパラメータに依存する名前については、インスタンス化時の引数の中にその型名が存在する場合か、その名前の前にtypenameキーワードが付けられている場合にのみ、コンパイラはその名前を型として扱います。この2つの場合以外、型として扱われることは決してありません。

したがって、T::iteratorが変数ではなく型であることをコンパイラに直接伝えたい場合は、typenameで修飾するだけです:

template <class T>
void foo() {
    typename T::iterator * iter;
}

これでコンパイラはT::iteratorが型であると確定でき、インスタンス化の時期まで待つ必要がなくなり、前述の曖昧さが解消されます。

ソースコード解析

STLのソースコードに戻りましょう。

template<typename _Iterator>
struct iterator_traits
{
  typedef typename _Iterator::iterator_category iterator_category;
  typedef typename _Iterator::value_type        value_type;
  typedef typename _Iterator::difference_type   difference_type;
  typedef typename _Iterator::pointer           pointer;
  typedef typename _Iterator::reference         reference;
};

上を見れば一発でわかります。typenameを使ってコンパイラに_Iterator::iterator_categoryが型であることを伝え、typedefで名前を付け直しているだけです。残りも同様です!

関連トピック

コメント