C++ typename の 2 つの使い方
C++ のテンプレートでは typename キーワードを使います。
ただ、typename はよく使うテンプレートパラメーター以外のところでも必要になってくることがあります。
そこで、今回は typename の使い方について紹介します。
テンプレートパラメーターでの typename
まずはよくあるテンプレートパラメーターでの使用例です。template <typename T> class Point { T x_; T y_; :テンプレートのパラメーター T が 型であることを宣言 するために typename が使われています。
ここでは typename の代わりに class を使うこともできます。 どちらを使っても違いは何もありません。
個人的には次のように使い分けています。
キーワード | 使用するケース |
---|---|
typename | パラメーターとして基本型をいれることを想定 |
class | それ以外 |
ただ、基本型を含めてすべての型はクラスとして考えるべき という意見の人などもいて、 どっちを使うかは個人の好みやコーディング規約で決めてください。
階層を持つテンプレートのための typename
次にテンプレートパラメーター以外での使い方です。例として、先ほどの点クラスの配列(vector)を持つ多角形(Polygon)クラスを考えてみます。
typename.cpp:
template <typename T> class Polygon { std::vector< Point<T> > points_; public: using value_type = T; using size_type = typename std::vector< Point<T> >::size_type; using iterator = typename std::vector< Point<T> >::iterator; using const_iterator = typename std::vector< Point<T> >::const_iterator; size_type size() const { return points_.size(); } iterator begin() { return points_.begin(); } iterator end() { return points_.end(); } const_iterator begin() const { return points_.begin(); } const_iterator end() const { return points_.end(); } :vector と同じような機能が欲しいので、vector のメソッドを委譲して使っています。
イテレーターを使うときなど、いちいち
std::vector< Point<T> >::iterator
と書くのが面倒なので、
using を使って別名保存しています。こういった using の使い方を知らなかったという人も typedef の上位互換だと思ってください。
1 つめの using に対して特に問題はありません。
2 つ目以降は typename を付けています。 これがテンプレートパラメーター以外の typename です。 これを付けていないとコンパイルが通りません。
typename.cpp:38:23: error: need 'typename' before 'std::vector<Point<T> >::size_type' because 'std::vector<Point<T> >' is a dependent scope using size_type = std::vector< Point<T> >::size_type;上記は g++ のコンパイルエラーメッセージですが、分かりづらいので、もう少し説明してみます。
T はテンプレートパラメーターなので、不確定です。 そのため Point<T> も不確定なので、std::vector<???>::size_type という形です。
"クラス名::xxxx" という形式は型だけでなく、定数にも使います。 そのため、型かどうかハッキリしないものに対して別名定義である using(typedef) を使うと、コンパイルエラーとなってしまいます。
コンパイルを通すには typename を記述して、次にくるのが型だとコンパイラーに伝える必要があります。
テンプレートをいろいろ使っていくと、 テンプレートクラスをメンバーに持ったテンプレートクラスのように階層を持つテンプレートというのが出てきます。 そういった場合、この型だということを宣言する typename の使い方も覚えておく必要があります。
typename Foo<T>::xxxx