Step By Step(C++テンプレートパラメータ)

26778 ワード

一、テンプレートクラスの静的データメンバーの定義:
次のコードでは、テンプレートベースの単一インスタンスクラスSingletonClassを与え、単一インスタンスの取得と単一インスタンスの解放の2つの静的メソッドを与える.このように、単一インスタンス機能を有する必要がある他のクラスに対して、クラスを直接継承することは、同様に単一インスタンス機能を有することができ、このテクニックは参照カウント機能にも同様に適用することができる.次の例では、テンプレートクラスに単一のインスタンスオブジェクトを表す静的メンバーを宣言します.通常のクラスの静的メンバーと同様に、外部で定義する必要がありますが、定義されたルールは文法的に通常のクラスでは少し異なります.これは、次の例で説明できます.
 1     #include <stdio.h>

 2     

 3     template<typename T>

 4     class SingletonClass {

 5     public:

 6         static T* GetInstance() {

 7             if (NULL == _instance)

 8                 _instance = new T();

 9             return _instance;

10         }

11         static void ReleaseInstance() {

12             if (NULL != _instance) {

13                 delete _instance;

14                 _instance = NULL;

15             }

16         }

17     private:

18         static T* _instance;

19     };

20     

21     template<typename T>

22     T* SingletonClass<T>::_instance = NULL;

23     

24     class MySingletonClass : public SingletonClass<MySingletonClass> {

25     public:

26         MySingletonClass() {}

27         ~MySingletonClass() {}

28     

29     public:

30         void DoPrint() {

31             printf("This is MySingletonClass.
"); 32 } 33 }; 34 35 int main() 36 { 37 MySingletonClass* myClass = MySingletonClass::GetInstance(); 38 myClass->DoPrint(); 39 MySingletonClass::ReleaseInstance(); 40 return 0; 41 }

二、虚メンバー関数:
テンプレートクラスのメンバーテンプレート関数は虚関数ではありません.虚関数呼び出しメカニズムの一般的な実装では、各虚関数がテーブルのエントリに対応するサイズ固定テーブルが使用されているためです.しかしながら、メンバーテンプレート関数のインスタンス化個数は、プログラム全体がコンパイルされるまで待たなければ最終的に決定されません.すなわち、メンバーテンプレート関数がコードによって呼び出されていない場合、関数はコンパイルに参加しないため、対応する関数エントリもありません.これに鑑みて,虚関数テーブルが固定サイズを必要とする制限と衝突した.しかしながら、テンプレートクラス内の一般メンバー関数は、一般メンバー関数がテンプレートクラスとともにコンパイルされるため、虚関数とすることができることを明確に指摘する必要がある.次のコード例を参照してください.
 1     #include <stdio.h>

 2     

 3     template<typename T>

 4     class TestClass {

 5     public:

 6         virtual ~TestClass() {}  //            ,        。

 7     

 8     public:

 9         template<typename T2>

10         virtual void DoTest(T2 const& ) {} //vs2010          : 'member function templates cannot be virtual'

11     };

12     

13     int main()

14     {

15         TestClass<int> c;

16         return 0;

17     }

三、テンプレートパラメータ:
C++のテンプレートパラメータは、通常タイプパラメータ、非タイプパラメータ、テンプレートタイプのテンプレートパラメータとすることができる.通常のタイプパラメータでは、typedef定義のタイプ名と見なすことができます.したがって、このタイプのパラメータは、次のようなタイプの宣言には使用できません.
templateclass TestClass{......friend class;//という声明は間違っています.    } 
非タイプパラメータの場合、現在のC++の標準は、template//非タイプパラメータclass TestClass{......}前例のtypename T::InternalTypeでは、typenameキーワードが修飾されていても、ここでtypenameの関数は前とは異なり、InternalTypeがテンプレートパラメータの内部定義のタイプであることを識別するために使用され、同時にポインタであるため、非タイプパラメータと見なす必要がある.非タイプパラメータについては、非タイプパラメータは右値のみ、すなわちアドレスも付与もできないことを明確に説明する必要がある.テンプレートのテンプレートパラメータについては、ほとんどの宣言と使用方法は通常のタイプパラメータと同じであり、明らかな違いは、テンプレートのテンプレートパラメータのタイプパラメータは、その外部のテンプレートクラスでは使用できません.ここで、TはテンプレートクラスMyTestタイプパラメータTestのタイプパラメータであるため、MyTestでは直接使用できない.}最後の点は、デフォルトのテンプレートパラメータについてです.1つの重要な制約は、デフォルトパラメータはtemplate-idリストで以前に宣言されたタイプパラメータにのみ依存し、現在のタイプパラメータに直接依存することはできません.たとえば、template>class TestClass{......}上記のコードのデフォルトテンプレートパラメータDefaultT 3については、そのタイプのパラメータは、この間に宣言されたT 1とT 2にのみ依存し、その表現されたT 3に直接依存することはできません.四、テンプレートの実例:C++コンパイラがテンプレートパラメータを実例化する方法は主に3種類ある:テンプレートの実例を表示する.デフォルトのテンプレートパラメータとダイナミックプッシュパラメータ.次のコードの例を示します.
1     template<typename T>

2     T const& max(T const& a, T const& b) {

3         return a < b ? b : a;

4     }

5     int main() {

6         max<double>(1.0,2.0);   //

7         max(1.0,2.0);           //          double

8         max<int>(1.0,2.0);      //         ,           int。

9     }

動的な推論方式では、あるタイプのパラメータが永遠に推論されない場合は、テンプレートのパラメータリストの前に配置することを考慮することができます.推論できるタイプのパラメータについては、できるだけ後ろに配置する必要があります.これにより、指定する必要があるタイプのパラメータを表示するだけで、残りのパラメータは、推論によってインスタンス化することができます.例えば、templateinline ReturnType Test(ParamType x){returnx;}次に、テンプレートパラメータの推論に関する実用的なテクニックを示します.すなわち、テンプレート関数に関数のリロードがある場合、コンパイラが選択したリロード関数をどのように判別すればいいのでしょうか.
 1     #include <stdio.h>

 2     

 3     template<typename T>

 4     int Test(T v) {

 5         return v;

 6     }

 7     

 8     template<typename T>

 9     double Test(T v, T v2) {

10         return v;

11     }

12     

13     int main() {

14         //

15         printf("This size of return value is %d.
",sizeof(Test(0,0))); 16 printf("This size of return value is %d.
",sizeof(Test(0))); 17 return 0; 18 }

テンプレートのパラメータは、インスタンス化中に派生クラスからベースクラスへの変換を考慮しません.次のコードを参照してください.
 1     #include <stdio.h>

 2     

 3     class Base {

 4     public:

 5         virtual void DoTest() {

 6             printf("This is Base::DoTest()
"); 7 } 8 }; 9 10 class Derive : public Base { 11 public: 12 void DoTest() { 13 printf("This is Derive::DoTest()
"); 14 } 15 }; 16 17 template<typename T, T* param> 18 class TestClass { 19 public: 20 void DoTest() { 21 param->DoTest(); 22 } 23 }; 24 25 Derive d; 26 int main() 27 { 28 TestClass<Base,&d> test; // T Base, Derive 。 29 test.DoTest(); 30 return 0; 31 }

以上のコードをコンパイルする際、vs 2010で与えられるコンパイルエラーは、'specialization':cannot convert from'Derive*'to'Base*'5、友元:友元自体の概念についてはここではあまり説明しないが、テンプレートクラスにおける友元と一般クラスにおける友元の文法規則上の違いを紹介するだけである.    1. テンプレート宣言のインスタンスを他のクラスの友元として宣言する場合は、テンプレートクラスが宣言されている必要がありますが、通常のクラスにはこのような制限はありません.次のようになります.
 1     template<typename T>

 2     class TemplateFriendClass {

 3         ... ...

 4     }

 5     

 6     template<typename T>

 7     class TestTest {

 8         friend class NormalFriendClass;      //    ,   NormalFriendClass        。

 9         friend class TemplateFriendClass<T>; //   TemplateFriendClass             。

10         ... ...

11     }

    2. この違いは、以前に宣言されたテンプレート関数であっても、そのテンプレート関数が事前に宣言されている場合、インスタンス化された友元関数が、以前に宣言されたテンプレート関数に推論され、一致しないと、コンパイラがエラーを報告するという、前の引用によるものです.友元テンプレート関数はここで定義できないためです.次のコードを参照してください.
1     template<typename T1,typename T2>

2     void TestFunc(T1,T2);

3     

4     class TestClass {

5         friend void TestFunc<>(int&,int&);             //OK.               TestFunc。

6         friend void TestFunc<int,double>(int,double);  //OK.              。

7         friend void TestFunc<char>(char&,int); //  ,                ,             。

8         friend void TestFunc(int,int) {}       //  ,           。

9     }

    3. テンプレートクラスで友元関数を宣言する場合は、次のシーンに注意してください.
 1     template<typename T>

 2     class TestClass {

 3         friend void TestFunc() {

 4             ... ...

 5         }

 6     };

 7     int main() {

 8         TestClass<void> t1;   //  TestClass          ,      TestFunc     。

 9         TestClass<double> t2; //  double       TestClass,        TestFunc  。

10         return 0;

11     }

前述の例から分かるように、テンプレートクラス変数を2回定義すると、TestFunc関数はテンプレートクラスTestClassに従って異なるタイプのインスタンス化に基づいて2回定義される.これにより、コンパイルエラーが発生します.次に、正しい方法を示します.    template    class TestClass {        friend void TestFunct(T*) {            ... ...        }    };改訂されたコードでは、TestFunc関数はテンプレート関数ではなく通常の関数ですが、彼の関数パラメータタイプは外部クラスのテンプレートパラメータに依存しているため、この関数はここで定義できます.また、関数パラメータのタイプによって定義される記号も異なるため、テンプレートクラスTestFuncを複数回インスタンス化しても、友元関数TestFuncは異なるタイプのパラメータに基づいて定義されます.        4. 友元テンプレートを宣言すると、テンプレートは基本テンプレートにすぎませんが、宣言後、基本テンプレートに対応したい特化テンプレートと一部の特化テンプレートも自動的に友元と見なされます.次のコード例とキーコメントを参照してください.
 1     #include <stdio.h>

 2     

 3     class TestClass {

 4     private:

 5         static void DoPrint() {

 6             printf("This is a private function of class TestClass.
"); 7 } 8 // FriendClass 。 9 template<typename T> friend class FriendClass; 10 }; 11 12 // , FriendClass DoTest TestClass 。 13 template<typename T> 14 class FriendClass { 15 public: 16 static void DoTest() { 17 TestClass::DoPrint(); 18 } 19 }; 20 // FriendClass FriendClass , DoTest 21 //TestClass DoPrint()。 22 template<> 23 class FriendClass<double> { 24 public: 25 static void DoTest() { 26 TestClass::DoPrint(); 27 } 28 }; 29 30 int main() 31 { 32 FriendClass<int>::DoTest(); 33 FriendClass<double>::DoTest(); 34 return 0; 35 }