回答:
以下はJosuttisの本からの引用です:
このキーワード
typename
は、後に続く識別子がタイプであることを指定するために導入されました。次の例を検討してください。template <class T> Class MyClass { typename T::SubType * ptr; ... };
ここで
typename
は、SubType
がのタイプであることを明確にするために使用されclass T
ます。したがって、ptr
は型へのポインタT::SubType
です。なしtypename
でSubType
は、静的メンバーと見なされます。したがってT::SubType * ptr
値の乗算になる
SubType
タイプのT
とptr
。
スタン・リップマンのブログ投稿は次のことを示唆しています:-
Stroustrup は、既存のプログラムを破壊する可能性のある新しいキーワードを導入するのではなく、既存のクラスキーワードを再利用して型パラメーターを指定しました。新しいキーワードが考慮されなかったわけではありません。混乱を招く可能性があるため、必要とされなかっただけです。そして、ISO-C ++標準までは、これが型パラメーターを宣言する唯一の方法でした。
したがって、基本的にStroustrupは、次の理由で標準で後で変更される新しいキーワードを導入せずにクラスキーワードを再利用しました
与えられた例として
template <class T>
class Demonstration {
public:
void method() {
T::A *aObj; // oops …
// …
};
言語文法がT::A *aObj;
算術式として誤って解釈されるため、新しいキーワードが導入され、typename
typename T::A* a6;
後続のステートメントを宣言として扱うようにコンパイラーに指示します。
キーワードは給与計算にあったので、クラスキーワードを再利用するという最初の決定によって引き起こされた混乱を修正してみませんか。
だから両方ある
あなたはこの投稿を見ることができます、それは間違いなくあなたを助けます、私はできるだけそれから抽出しました
typename
でもclass
、同じ目的で既存のキーワードを使用できるのに、なぜ新しいキーワードが必要だったのでしょうか。
typename
Josuttisを引用することによるNaveenの回答で説明されているように、解析の問題を修正するために必要になりました。(私は挿入するとは思わないclass
この場所では、働いていると思います。)新しいキーワードはこの場合のために受理された後にのみ、それはまた、テンプレート引数の宣言では許可され(たか、その定義はある?)、というためclass
常に多少ありました誤解を招く。
いわゆる依存型(「テンプレートパラメーターに依存」を意味する)のメンバーを参照する状況では、コンパイラーは、結果の構成の意味論的意味を常に明確に推測できるわけではありません。 (つまり、型の名前、データメンバーの名前、または何か別の名前かどうか)。そのような場合は、その名前がその依存型のメンバーとして定義されているタイプ名に属していることをコンパイラーに明示的に伝えて、状況を明確にする必要があります。
例えば
template <class T> struct S {
typename T::type i;
};
この例ではtypename
、コードをコンパイルするために必要なキーワードです。
依存型のテンプレートメンバー、つまりテンプレートを指定する名前を参照する場合も同じことが起こります。キーワードを使用してコンパイラを支援する必要もありますが、template
配置は異なります
template <class T> struct S {
T::template ptr<int> p;
};
場合によっては、両方を使用する必要があります
template <class T> struct S {
typename T::template ptr<int>::type i;
};
(私が構文を正しく取得した場合)。
もちろん、キーワードの別の役割は、typename
テンプレートパラメータ宣言で使用されます。
その秘密は、テンプレートが特定のタイプに特化できるという事実にあります。つまり、いくつかのタイプで完全に異なるインターフェースを定義することもできます。たとえば、次のように書くことができます。
template<typename T>
struct test {
typedef T* ptr;
};
template<> // complete specialization
struct test<int> { // for the case T is int
T* ptr;
};
なぜこれが実際に有用であるのかを尋ねる人がいるかもしれません。それは本当に役に立たないようです。ただし、たとえばstd::vector<bool>
、reference
型が他T
のと完全に異なるように見えることに注意してください。確かに、それは種類を別の種類に変更しませんreference
が、それでも発生する可能性があります。
次に、このtest
テンプレートを使用して独自のテンプレートを作成するとどうなりますか。このようなもの
template<typename T>
void print(T& x) {
test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
あなたはそれtest<T>::ptr
がタイプであることを期待しているのでそれはあなたにとって大丈夫であるようです。しかし、コンパイラはそれを知りませんし、実際には彼は標準では反対を期待するようにアドバイスさえされておりtest<T>::ptr
、タイプではありません。コンパイラーに、何を追加する必要があるかを予測するには、typename
前に追加する必要があります。正しいテンプレートは次のようになります
template<typename T>
void print(T& x) {
typename test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
結論:typename
テンプレートでネストされたタイプのテンプレートを使用する場合は、常に追加する必要があります。(もちろん、テンプレートのテンプレートパラメータがその内部テンプレートに使用されている場合のみ。)
すべての答えは、 typename
キーワードは2つの異なるケースで使用されます。
a)テンプレート型パラメーターを宣言するとき。例えば
template<class T> class MyClass{}; // these two cases are
template<typename T> class MyNewClass{}; // exactly the same.
それらの間に違いはなく、それらはまったく同じです。
b)テンプレートにネストされた依存型名を使用する前。
template<class T>
void foo(const T & param)
{
typename T::NestedType * value; // we should use typename here
}
使わない typename
、解析/コンパイルエラーが発生します。
Scot Meyersの本「Effective C ++」で述べられているように、2番目のケースに追加したいのは、ネストされた依存型名のtypename
前に使用する例外があることです。例外は、ネストされた依存型名を基本クラスとして、またはメンバー初期化リストで使用する場合は、そこで使用しないでください。typename
template<class T>
class D : public B<T>::NestedType // No need for typename here
{
public:
D(std::string str) : B<T>::NestedType(str) // No need for typename here
{
typename B<T>::AnotherNestedType * x; // typename is needed here
}
}
注:typename
C ++ 20以降、2番目のケース(つまり、ネストされた依存型名の前)に使用する必要はありません。
#include <iostream>
class A {
public:
typedef int my_t;
};
template <class T>
class B {
public:
// T::my_t *ptr; // It will produce compilation error
typename T::my_t *ptr; // It will output 5
};
int main() {
B<A> b;
int my_int = 5;
b.ptr = &my_int;
std::cout << *b.ptr;
std::cin.ignore();
return 0;
}