web-dev-qa-db-ja.com

「typename」キーワードはいつ必要ですか?

可能性のある複製:
正式には、typenameは何のためですか?
テンプレートとtypenameキーワードをどこに、なぜ配置する必要があるのですか?

以下のコードを検討してください。

template<class K>
class C {
    struct P {};
    vector<P> vec;
    void f();
};

template<class K> void C<K>::f() {
    typename vector<P>::iterator p = vec.begin();
}

この例で「typename」キーワードが必要なのはなぜですか? 「typename」を指定する必要がある他のケースはありますか?

50
Martin

簡単な答え:従属名であるネストされた名前を参照するときは常に、つまり、未知のパラメーターを持つテンプレートインスタンス内にネストされます。

長い答え:C++には、値、型、およびテンプレートの3つの層のエンティティがあります。それらはすべて名前を持つことができ、名前だけではどの層のエンティティかはわかりません。むしろ、名前のエンティティの性質に関する情報は、コンテキストから推測する必要があります。

この推論が不可能な場合は、必ず指定する必要があります。

template <typename> struct Magic; // defined somewhere else

template <typename T> struct A
{
  static const int value = Magic<T>::gnarl; // assumed "value"

  typedef typename Magic<T>::brugh my_type; // decreed "type"
  //      ^^^^^^^^

  void foo() {
    Magic<T>::template kwpq<T>(1, 'a', .5); // decreed "template"
    //        ^^^^^^^^
  }
};

ここでは、Magic<T>::gnarlMagic<T>::brugh、およびMagic<T>::kwpqの名前を明示する必要がありました。これは、伝えることが不可能なためです。Magicはテンプレートであるため、非常にタイプ_natureの性質Magic<T>Tに依存します-たとえば、プライマリテンプレートとはまったく異なる特殊化が存在する場合があります。

Magic<T>::gnarlを従属名にしているのは、Tが不明なテンプレート定義内にいるという事実です。 Magic<int>を使用した場合、コンパイラはMagic<int>の完全な定義を知っているので(お約束します!)、これは異なります。

(これを自分でテストしたい場合は、使用できるMagicのサンプル定義を以下に示します。簡潔にするためにconstexprを使用することはご容赦ください。古いコンパイラをお持ちの場合は、お気軽に変更してください。古いスタイルのC++ 11以前の形式への静的メンバー定数宣言。)

template <typename T> struct Magic
{
  static const T                    gnarl;
  typedef T &                       brugh;
  template <typename S> static void kwpq(int, char, double) { T x; }
};
template <> struct Magic<signed char>
{
  // note that `gnarl` is absent
  static constexpr long double brugh = 0.25;  // `brugh` is now a value
  template <typename S> static int kwpq(int a, int b) { return a + b; }
};

使用法:

int main()
{
  A<int> a;
  a.foo();

  return Magic<signed char>::kwpq<float>(2, 3);  // no disambiguation here!
}
70
Kerrek SB

typenameiteratorの依存型であるため、Pキーワードが必要です。コンパイラは、iteratorが値または型を参照しているかどうかを推測できないため、typenameを叫ばない限り、その値を想定します。型または値のいずれかが有効であるコンテキストで、テンプレート引数に依存する型がある場合は常に必要です。たとえば、基底クラスは型でなければならないため、基底クラスtypenameは必要ありません。

同じテーマで、いくつかの依存名が値ではなくテンプレート関数であることをコンパイラーに知らせるために使用されるtemplateキーワードがあります。

11
K-ballo

Typenameキーワードは、型名がテンプレートパラメーターに依存する場合に必要です(したがって、コンパイラーは識別子のセマンティクスを「知る」ことができます(typeまたはvalue)最初のパスで完全なシンボルテーブルを持たない)。


同じ意味ではなく、少し一般的ではありませんが、lone typenameキーワードは、汎用テンプレートパラメータを使用する場合にも役立ちます。 http: //ideone.com/amImX

#include <string>
#include <list>
#include <vector>

template <template <typename, typename> class Container,
          template <typename> class Alloc = std::allocator>
struct ContainerTests 
{
    typedef Container<int, Alloc<int> > IntContainer;
    typedef Container<std::string, Alloc<int> > StringContainer;
    //
    void DoTests()
    {
        IntContainer ints;
        StringContainer strings;
        // ... etc
    }
};

int main()
{
    ContainerTests<std::vector> t1;
    ContainerTests<std::list>   t2;

    t1.DoTests();
    t2.DoTests();
}
8
sehe