C++Primer学習ノート_45_テンプレート(3):デフォルトテンプレートパラメータ(標準テンプレートコンテナdequeによるStackテンプレート)、メンバーテンプレート、キーワードtypename


一、デフォルトテンプレートパラメータ
1、stackメモリは標準テンプレートコンテナで管理できますか?答えは肯定的で、1つのテンプレートパラメータを追加するだけでよく、テンプレートパラメータはデフォルトで、以下のようにすることもできます.
template <typename T, typename CONT = std::deque<T> > //         ,      
class Stack
{
…
private:
    CONT c_;
};

2番目のパラメータが送信されていない場合は、デフォルトでdeque両端キューです.もちろんstd::vectorを渡すこともできます.
2、例:標準テンプレートコンテナdeque管理によるstackテンプレートクラスの実装
ここのStackはアダプタで、STLの6つのコンポーネントの1つで、コードは多重化して、継承を通じてではありません;既存のクラスによるテンプレートクラスの実装
Stack.h:
#ifndef _STACK_H_
#define _STACK_H_

#include <exception>
#include <deque>
using namespace std;

template <typename T, typename CONT = deque<T> > //         ,      
class Stack
{
public:
    Stack() : c_()
    {
    }
    ~Stack()
    {
    }

    void Push(const T &elem)
    {
        c_.push_back(elem);
    }
    void Pop()
    {
        c_.pop_back();
    }
    T &Top()
    {
        return c_.back();
    }
    const T &Top() const
    {
        return c_.back();
    }
    bool Empty() const
    {
        return c_.empty();
    }
private:
    CONT c_;  //         deque     stack
};

#endif // _STACK_H_

main.cpp:
#include "Stack.h"
#include <iostream>
#include <vector>
using namespace std;


int main(void)
{
    //Stack<int> s;  //   deque  
    Stack<int, vector<int> > s;
    s.Push(1);
    s.Push(2);
    s.Push(3);

    while (!s.Empty())
    {
        cout << s.Top() << endl;
        s.Pop();
    }
    return 0;
}

出力は3 2 1
すなわち、2番目のパラメータが渡されていない場合、スタックやスタックなどの操作はdequeのメンバー関数を直接呼び出し、dequeによってメモリも管理されます.
プログラムでvectorが渡されるとvectorメンバー関数で処理されます.
二、メンバーテンプレート
次の例を見てみましょう.
#include <iostream>
using namespace std;


template <typename T>
class MyClass
{
private:
    T value;
public:
    void Assign(const MyClass<T> &x)
    {
        value = x.value;
    }
};

int main(void)
{
    MyClass<double> d;
    MyClass<int> i;

    d.Assign(d);        // OK
    d.Assign(i);        // Error
    return 0;
}

iとdのタイプが異なるため、コンパイルエラーが発生します.メンバーテンプレートの方法で解決できます.
#include <iostream>
using namespace std;

template <typename T>
class MyClass
{
private:
    T value;
public:
    MyClass() {}
    template <class X>
    MyClass(const MyClass<X> &x) : value(x.GetValue())
    {

    }
    template <class X>
    void Assign(const MyClass<X> &x)
    {
        value = x.GetValue(); //      ,value  ,         
    }
    T GetValue() const
    {
        return value;
    }
};

int main(void)
{
    MyClass<double> d;
    MyClass<int> i;
    d.Assign(d);        // OK
    d.Assign(i);        // OK

    MyClass<double> d2(i);

    return 0;
}

MyClassd 2(i);したがって、d=iをサポートしたい場合は、コピーコンストラクション関数をメンバーテンプレート関数として実装する.割り当て演算子をメンバーとして実装するには
テンプレート.
実際にはauto_ptrのインプリメンテーションでは、次のような演算をサポートするため、メンバーテンプレートが使用されます.
auto_ptr x;
auto_ptr y;
x = y;
三、typenameキーワード
次の例を見てください.
typename T::SubType *ptr_;前にtypename修飾がなければ,SubTypeはT型内部の静的データメンバーとして扱われ,推論すると*はポインタではなく乗数として扱われ,コンパイル時にエラーとなる.修飾を加えると、SubTypeはT内部のカスタムタイプであり、ptrはこのタイプへのポインタであり、コンパイルは通過する.
#include <iostream>
using namespace std;

template <typename T>
class MyClass
{
private:
    typename T::SubType* ptr_;
};

class Test
{
public:
    typedef int SubType;  //  Test     SubType  ,      
};

int main(void)
{
    MyClass<Test> mc;
    return 0;
}

四、派生クラスとテンプレート、オブジェクト向けと汎用プログラミング
(一)、派生クラスとテンプレート
1、実行の効率のために、クラステンプレートは互いに独立している、すなわち独立した設計であり、継承の思想を使用していない.クラステンプレートの拡張は、dequeを使用してStackを実装するなど、アダプタ(adapter)を使用して行われています.汎用性はテンプレートライブラリの設計の出発点の一つであり,これは汎用アルゴリズム(algorithm)や関数オブジェクト(functor)などの手段によって達成される.
2、派生の目标の1つもコードの多重化とプログラムの通用性で、最も典型的なのはMFCで、派生类の利点は简単から繁まで、次第に深く入り込むことができて、プログラムの编制の过程の中で前の仕事を十分に利用することができて、一歩一歩复雑な任务を完成します.
3、テンプレートは運行効率を追求し、派生はプログラミングの効率を追求する.
(二)、オブジェクト向けと汎用プログラミング
1、対象と汎用はいずれもある形式の多態に依存する
(1)オブジェクト向け
ダイナミックマルチステート
(2)汎用型
スタティツクマルチステート
2、オブジェクト向けのマルチステートは、実行時に継承関係が適用されます.これらのクラスを使用するコードを作成し、ベースクラスと派生クラスのタイプの違いを無視します.ベースクラスポインタまたは参照を使用する限り、ベースクラスタイプオブジェクト、派生クラスタイプオブジェクトは同じコードを共有できます.
3、汎用プログラミングでは、私たちが作成したクラスと関数は、コンパイル時に関連しないタイプに多態的に使用することができます.1つのクラスまたは1つの関数を使用して、複数のタイプのオブジェクトを操作できます.
参照先:
C++primer第4版Effective C++3 rd C++プログラミング仕様