【極客班】『STLと汎用プログラミング第1週』学習ノート

4912 ワード

1.テンプレートの概要
1)c++テンプレートの概要
c++では、テンプレートは、関数またはクラスが汎用的に表現または実行されることを可能にすることができる.たとえば、max関数を使用して2つの整数の最大値を計算できます.コードは次のとおりです.
int Max(int x, int y)
{
  return x > y ? x : y;  
}

しかし、double、longなどの他のタイプに対してもMax関数を定義したい場合は、類似コードを繰り返し定義する場合があります.関数テンプレートを使用して、次のように定義できます.
template  T Max(T a, T b)
{
  return a > b ? a : b;
}

実際に関数テンプレートを使用する場合は、以前の方法でMax関数を使用できます.
cout << Max(33, 25) << endl;

c++では、テンプレートはクラステンプレートと関数テンプレートの2つに分けることができます.テンプレートには、テンプレート宣言とテンプレートインスタンス化という2つの重要な概念もあります.テンプレート宣言は、関数またはクラスのテンプレート形式を与える定義です.テンプレートのインスタンス化は、テンプレートから真のクラスまたは関数を構築するプロセスです.前のMax関数テンプレートの定義はテンプレート宣言であり、後にMax(33,25)が呼び出されるとテンプレートインスタンス化が実行されます.テンプレートのインスタンス化には、次の2つの方法があります.
1.明示的なインスタンス化
インスタンス化に使用するタイプをコードに明示的に指定
2.暗黙的インスタンス化
テンプレートを初めて使用するときに、状況に応じて自動的に適切なタイプを選択してインスタンス化します.
2)c++関数テンプレート
前のMaxは、次のように定義することもできます.
template  T Max(T a, T b)
{
  return a > b ? a : b;
}

しかしclassとtypenameには異なる意味があり、typenameを使用するのが望ましい.前述したテンプレートのインスタンス化は、テンプレートパラメータを特定のタイプで置き換えるプロセスとしても理解できる.関数テンプレートのインスタンス化はコンパイラによって自動的に実現されます.テストするために小さなプログラムを書きました.
#include 
using namespace std;

template  T Max(T x, T y)
{
  return x > y ? x : y;
}

int main(void)
{
  cout << Max(33, 28) << endl;
}


このファイルはclangコンパイルを使用してアセンブリファイルを生成し、c++filtを使用してシンボルを元の関数に変換します.コマンドは次のとおりです.
clang++ -S max.cpp 
 cat max.s  | c++filt

アセンブリファイルの内容(キーのみ保持):
.Ltmp10:
    .cfi_def_cfa_register %ebp
    subl    $24, %esp
    movl    $33, %eax
    movl    $28, %ecx
    movl    $33, (%esp)
    movl    $28, 4(%esp)
    movl    %eax, -4(%ebp)          # 4-byte Spill
    movl    %ecx, -8(%ebp)          # 4-byte Spill
    calll   int Max(int, int)
    leal    std::cout, %ecx
    movl    %ecx, (%esp)
    movl    %eax, 4(%esp)
    calll   std::basic_ostream >::operator< >& std::endl >(std::basic_ostream >&), %ecx
    movl    %eax, (%esp)
    movl    %ecx, 4(%esp)
    calll   std::basic_ostream >::operator< >& (*)(std::basic_ostream >&))
......
int Max(int, int):                      # @int Max(int, int)
# BB#0:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $12, %esp
    movl    12(%ebp), %eax
    movl    8(%ebp), %ecx
    movl    %ecx, -4(%ebp)
    movl    %eax, -8(%ebp)
    movl    -4(%ebp), %eax
    cmpl    -8(%ebp), %eax
    jle .LBB2_2
......

アセンブリコードから分かるように,実際に呼び出されたのはint Max(int,int)である.また、コードセグメントの後ろには、このインスタンス化された関数テンプレートの定義があります.明らかに、関数テンプレートでは、インスタンス化された関数を使用するには、インスタンス化されたコードを生成する必要があります.関数テンプレートをインスタンス化する場合,パラメータのタイプに基づいてどのようにインスタンス化する必要があるかを判断する必要があり,この過程はパラメータ導出となる.パラメータの導出中は、自動タイプ変換は許可されません.たとえば、次の使い方が間違っています.
Max(1,2.0)

ただし、このようなエラーは、次のいずれかの方法でクラスで処理できます.
Max(static_cast(1,2.0)

関数テンプレートは、通常の関数と同様に再ロードでき、通常の関数と共存できます.非テンプレート関数とテンプレート関数の両方が適用される場合は、非テンプレート関数を優先的に選択します.Max<>(23,7)の使用を許可すると、前のMax(23,7)と等価なテンプレートインスタンス化のために適切なタイプを選択することが自動的に導出される.
3)クラステンプレート
クラステンプレートの一例:
const std::size_t DefaultStackSize = 1024;
template  class Stack{
public:
    void Push(const T const & element);
    int Pop(T &element);
    int Top(T &element) const;
private:
    std::vector m_Members;
    std::size_t m_nMaxSize = n;
};

クラステンプレート定義は、関数テンプレート定義と似ています.クラステンプレート関数定義では、クラス自体を使用する必要がある場合は、Tではなく完全な定義(Stack)が必要です.クラスのテンプレート関数を定義する場合は、Push関数などのテンプレート関数の定義を次のように指定する必要があります.
template 
void Stack::Push(const T const& element)
{
 ....
}

クラステンプレートの使用方法は次のとおりです.
Stack stack;  //        int   stack
Stack stack; //           int  ,       100.
Stack > intStackStack; //     ,     Stack

次のコードと同様に、クラステンプレートを特化できます.
template <>
class Stack<:wstring> {
...
};

クラステンプレートを偏特化することもできます(すなわち、一部のテンプレートタイプのみを特化します).
4)c++オペレータリロード
c++のキーワードoperatorは、*、->またはカッコオペレータなどの一般的な操作をリロードできる特殊な関数を定義します.オペレータのリロードは内部タイプでは使用できません.オペレータリロードは、非静的メンバー関数または静的グローバル関数に使用できます.一元オペレータのリロードはメンバー関数にパラメータがなく、二元オペレータのリロードはメンバー関数に1つのパラメータしかありません.オペレータのリロードにはデフォルトのパラメータは使用できません.
2.汎用プログラミング
1)概要
汎用プログラミングは、特定の言語に関係なく、呼び出しが必要なときにパラメータでタイプを与えることを可能にするプログラミング思想である.
2)Traits