STLを学習する過程で、次のような理解しにくいC ++コードに遭遇することがよくあります。
typedef typename std::vector<T>::size_type size_type;
タイプエイリアスを定義する必要があるように見えますが、typedefの使用方法はtypedef +元のタイプ名+新しいタイプ名である必要があります(typedefの特定の使用法は、typedefと#defineの使用法と違いに移動できます)。なぜそこにあるのですか。ここに追加のtypename ?? typenameとは何ですか?
typedef char* PCHAR;
ジェネリックプログラミング(GP)では、テンプレートクラス/関数を定義する場合、テンプレートまたはテンプレートを使用でき、2つは同等であることを覚えておいてください。
では、上記のステートメントのtypenameキーワードの効果は何ですか?
1. std :: vector :: size_typeとは何ですか?
HouJieの「STLSourceAnalysis」の本をチェックしてください。
template <class T,class Alloc=alloc>
class vector{
public:
//...
typedef size_t size_type;
//...
};
元のvector :: size_typeは、vector内のネストされた型であり、実際には、STLの広い領域で使用されるクロスプラットフォームのsize_t型と同等です。
typenameキーワードを使用することの重要性ステートメントでtypenameキーワードは別として、ステートメントは次のとおりです。
typedef std :: vector :: size_type size_type;
1
std :: vector :: size_typeの名前が変更されたように感じることがあります。では、なぜキーワードtypenameを追加するのでしょうか。
Tはテンプレートのタイプパラメータであることがわかります。T内のsize_typeは言うまでもなく、テンプレートがインスタンス化されたときに、それがどのタイプであるかだけがわかります。したがって、修飾された従属名(説明については補足を参照)std :: vector :: size_typeの場合、それが静的メンバー変数/関数であるかネストされた型であるかを判別することはできません。これにより、ステートメントにあいまいさが生じます。耐えられない。
std :: vector :: size_typeのタイプの前にキーワードtypenameが追加されている場合、これはネストされたタイプであり、静的メンバーまたはTの静的メンバー関数ではないことを示します。これによりあいまいさがなくなります。
- まとめ
上記の分析に基づいて、ステートメントの意味と機能を知ることができ
ます。typedefは既存の型のエイリアスを作成し、typenameはコンパイラにstd :: vector :: size_typeがメンバーではなく型であることを通知します。
拡張機能:typenameキーワードがあるのはなぜですか?typename
が追加されていない場合、次のテンプレート定義によりステートメントがあいまいになります。
template <class T>
void foo() {
T::iterator * iter;
// ...
}
struct ContainsAnotherType {
static int iterator;
// ...
};
次に、次のようにfooのtypeパラメーターをインスタンス化します。
foo<ContainsAnotherType>();
次に、T :: iterator * iter;は、ContainsAnotherType :: iterator * iter;としてコンパイラーによってインスタンス化されます。ここで、frontは型ではなく静的メンバー変数であり、これは乗算式になりますが、iterはここでは定義されておらず、コンパイラーはエラーを報告します。
error C2065: ‘iter’ : undeclared identifier
ただし、iterがグローバル変数の場合、このコード行は完全に正しいものになります。これは2つの数値の乗算を計算する式であり、戻り値は破棄されます。
その結果、同じコード行を2つのまったく異なる方法で解釈でき、テンプレートがインスタンス化される前にそれらを区別する方法はありません。
この問題を解決するために、C ++標準委員会はtypenameキーワードを導入しました。これにより、テンプレートのクラス/関数は、静的メンバー変数/関数がまだ存在するため、内部の依存名(例:vector :: iterator viter ;、依存名とも呼ばれます)を区別します。ネストされた型。
補足:クラスのメンバーまたは名前へのクラス外部アクセス、(非)修飾名、(非)従属名
1.メンバーまたはクラスの名前へのクラス外部アクセス
クラススコープの概念では、クラス外のクラスの名前にアクセスするときに、クラススコープ演算子::を使用できます。通常、MyClass :: name:①静的データメンバー、②の形式で3種類の呼び出しがあります。静的メンバー関数と③ネストタイプ:
struct MyClass {
static int A;
static int B();
typedef int C;
}
MyClass :: A、MyClass :: B、MyClass :: Cは、それぞれ上記の3つのタイプに対応します。
2つの修飾名と非修飾名
修飾名は、名前空間を定義する名前です。次のコードを見てください。coutとendlは修飾名です。
#include <iostream>
int main() {
std::cout << "Hello world!" << std::endl;
}
coutとendlの両方の前にstd ::があり、これによりstd名前空間が制限されるため、修飾名と呼ばれます。
上記のコードで、std :: cout;を使用するか、名前空間std;を前に使用する場合は、使用するときにcoutとendlのみを使用し、std ::を制限するためにそれらの前にスペースがないため、coutとendl現時点では、これは無条件の名前と呼ばれています。
3つの従属名と非従属名
依存名とはテンプレートパラメータに依存する名前を指し、非依存名とはテンプレートパラメータに依存しない名前を指します。次のコードを見てください。
template <class T>
class MyClass {
int i;
vector<int> vi;
vector<int>::iterator vitr;
T t;
vector<T> vt;
vector<T>::iterator viter;
};
これは組み込み型であるため、テンプレートクラスが宣言されると、クラスの最初の3つの定義済み型が認識されます。ただし、次の3行の定義では、それらはすべてテンプレートパラメータTに依存しているため、テンプレートがインスタンス化されたときにのみタイプを知ることができます。したがって、T、vector、vector :: iteratorは依存関係名と呼ばれます。最初の3つの定義は、非依存名と呼ばれます。
さらに複雑なことに、typedef TU; U u;が使用されている場合、Tは再び表示されませんが、Uは依然として依存名です。直接的であれ間接的であれ、テンプレートパラメータに依存している限り、名前は依存名であることがわかります。
リファレンスブログ:
typedeftypenameの役割