C++のテンプレートにおけるtypenameキーワードの使い方を詳しく説明する

4203 ワード

typenameの使用場面1は,テンプレート定義に用いられ,その後のテンプレートパラメータがタイプパラメータであることを示す.たとえば

template
T foo(const T& t, const Y& y){//....};

templace
class CTest
{
private:
 T t;
public:
 //...
}


実は、ここで最もよく使われるのはキーワードclassを使うことで、しかも両者の機能はまったく同じで、ここのclassとクラスを定義する時のclassはまったく別で、C++は当時キーワードを減らすためにclassを使っていました.しかし結局typenameを導入せざるを得なかったのは、いったい何が原因なのか.2つ目、つまりtypenameの2つ目の使い方を見てください.用途2、テンプレートに「埋め込み依存タイプ名」と表記されています.ここには3つの語があり、埋め込み、依存、タイプ名があります.では、「埋め込み依存タイプ名(nested dependent type name)」とは何ですか.SGI STLの一例を見てください.STLのcountモデルアルゴリズムの実現だけです.

template 
typename iterator_traits<_inputiter>::difference_type
count(_InputIter __first, _InputIter __last, const _Tp& __value) {
 __STL_REQUIRES(_InputIter, _InputIterator);
 __STL_REQUIRES(typename iterator_traits<_inputiter>::value_type,
         _EqualityComparable);
 __STL_REQUIRES(_Tp, _EqualityComparable);
 typename iterator_traits<_inputiter>::difference_type __n = 0;
 for ( ; __first != __last; ++__first)
  if (*__first == __value)
   ++__n;
 return __n;
}


ここではtypename:戻り値、パラメータ、変数定義の3つの場所で使用します.それぞれ:

typename iterator_traits<_inputiter>::difference_type
typename iterator_traits<_inputiter>::value_type
typename iterator_traits<_inputiter>::difference_type __n = 0;

difference_type, value_typeは_に依存するInputIter(テンプレートタイプパラメータ)のタイプ名.ソースコードは次のとおりです.

template 
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;
};

埋め込みとは、クラス名の定義に定義されます.以上difference_typeとvalue_typeはiterator_に定義されていますtraitsの中の.依存とは、テンプレートパラメータに依存することです.typename iterator_traits<_inputiter>::difference_typeでdifference_typeはテンプレートパラメータ_に依存するInputIter. タイプ名とは、ここで最終的に指摘するタイプ名であり、変数ではありません.例えばiterator_traits<_inputiter>::difference_typeはクラスiteratorである可能性がありますtraits<_inputiter>クラスのstaticオブジェクト.そして私たちがこのように書くと、C++のデフォルトは変数として解釈されます.したがって、変数と区別するためにtypenameを使用してコンパイラに伝える必要があります.では、すべてのT::type_or_variable、またはtmpl:type_or_variableはtypenameを使う必要がありますか?いいえ、次の2つの例外があります.例外(1)クラステンプレート定義のベースクラスのリスト.たとえば

template
class Derived: public Base::XXX
{
  ...
}


(2)クラステンプレート定義における初期化リスト.

Derived(int x) : Base::xxx(x)
{
  ...
}


どうしてここにいらないの?コンパイラはここで必要とするタイプか変数かを知っているので,(1)ベースクラスリストにはタイプ名,(2)初期化リストにはメンバー変数名に違いない.
typenameとclassの違いはc++Templateではtypenameとclassの2つのキーワードが使われているところが多く、置き換えられるようですが、この2つのキーワードは完全に同じではないでしょうか.C++を学ぶ人はclassというキーワードをよく知っていると信じています.classはクラスを定義するために使用され、テンプレートにc++を導入した後、最初にテンプレートを定義する方法は:template......ここでclassキーワードはTが1つのタイプであることを示し、その後classのこの2つの場所での使用が混同をもたらす可能性を避けるためにtypenameというキーワードを導入し、classと同様に後の記号が1つのタイプであることを示す役割を果たし、テンプレートを定義する際に以下の方法を使用することができるようになった.
template...... テンプレート定義構文では、キーワードclassはtypenameと全く同じ役割を果たします.typenameはテンプレート定義だけで機能しますか?実はそうではありません.typenameのもう一つの役割は、ネスト依存タイプ(nested depended name)を使用することです.以下に示します.

class MyArray 
{ 
  public:
  typedef int LengthType;
  .....
}

template
void MyMethod( T myarr ) 
{ 
  typedef typename T::LengthType LengthType; 
  LengthType length = myarr.GetLength; 
}


このときtypenameの役割は、c++コンパイラに、typenameの後ろの文字列がメンバー関数やメンバー変数ではなくタイプ名であることを伝えることです.このとき、前にtypenameがなければ、コンパイラはT::LengthTypeはタイプであり、メンバー名(静的データメンバーまたは静的関数)であるため、コンパイルは通過できません.