C++ まとめ -言語編-

ページ内目録

テンプレート

設計方法の1つに,ジェネリックプログラミングというものがある.これは,特定のコンセプトに依存しない一般的な概念を表現することで,汎用的なモジュールを生成する.例えば,C++では変数はある型の値を保持する存在であるが,その概念は型の種類に依存しない.変数はあくまで何かを入れるための器であって,型はその中身の種類やサイズを決定しているだけである.C++では,このような一般的な概念をテンプレートという仕組みによって表現できる.テンプレートでは,型を引数(型引数)として受け取ることによって,コンセプトに依存しない一般的な概念を表現できる.まず,変数を表現した例を示す(実用性はまるでないが).


template<class type> class Variable
{
public:
  type val;
  show(){ std::cout << val << std::endl; }
  Variable(){}
  ~Variable(){}
};

int main()
{
  Variable<int> i;
  i.val = 10;
  i.show();

  Variable<double> d;
  d.val = 3.141592;
  d.show();
  
  return 0;
}
これ以外にも,リスト構造などの概念が一般化できる.

テンプレートの定義

テンプレートを定義するための正しい文法は,次の通りである.

template-declaration
exportopt template <template-argument-list > declaration
template-argument-list
  • template-argument
  • template-argument-list , template-argument
template-argument
  • type-argument
  • argument-declaration
type-argument
  • class identifieropt
  • class identifieropt = type-id
  • typename identifieropt
  • typename identifieropt = type-id
  • template <template-argument-list > class identifieropt
  • template <template-argument-list > class identifieropt = template-name

テンプレートのインスタンスの生成

テンプレートクラスとテンプレート引数を用いた宣言やテンプレート関数とテンプレート引数を用いた宣言は,テンプレートのインスタンス生成(template instantiation)と呼ばれる.テンプレートのインスタンスを生成する場合,宣言子(declarator) や抽象宣言子(abstract-declarator)内のid-expressiontemplate-idを使用する.

explicit-instantiation
template declaration
explicit-instatiation
template < > declaration
<未完成-_-.>
template-id
  • template-name <template-argument-listopt >
template-arguemnt-list
  • template-argument
  • template-argument-list , template-argument
template-name
  • identifier
type-argument
  • class identifieropt
  • class identifieropt = type-id
  • typename identifieropt
  • typename identifieropt = type-id
  • template <template-argument-list > class identifieropt
  • template <template-argument-list > class identifieropt = template-name
補足
  • type-id は型や型修飾子からなる列.
  • identifier は識別子(名前).
以下に実際にテンプレートを使用した例をいくつか示す.
クラステンプレートの場合

template <class T> class Var
{
public:
  T val;
  T operator=( T v ){ val = v; return v; }
  T show(){ std::cout << val << std::endl; return val; }
};


int main()
{
  Var<int> i;
  Var<char> c;

  i = 10;
  c = 'a';
  
  i.show();
  c.show();

  return 0;
}
関数テンプレートの場合

template<class T> T increment(T arg)
{
  return arg++;
}

int main()
{
  std::cout << increment<int>(11) << std::endl;//12
  std::cout << increment<char>('a') << std::endl;//b
  return 0;
}

テンプレート引数

テンプレートは,次のものをテンプレート引数として受け取ることができる.

  • 通常のオブジェクト
  • テンプレート
ユーザは,テンプレート引数として以下のものを与えることができる.
  • 定数式
  • 外部リンケージを持つオブジェクト
  • 関数のアドレス
  • メンバに対する多重定義されていないポインタ
テンプレート引数(template-argument)は,以降に現れるテンプレート引数の定義にも利用できる.

template <class T, T v> class Hoge { };
また,テンプレート引数として定数を与えることで,内部の配列のサイズなどをオブジェクト毎に変更することができる.

template <class T, unsigned int size> class Hoge
{
public:
  T vals[size];
};

int main()
{
  Hoge<int, 10> ary1;
  Hoge<char, 5> ary2;
  
  ary1.vals[0] = 10;
  ary2.vals[0] = 'a';
  return 0;
}
定数を引数として与えることは,これ以外にも最大値や最小値の指定に利用することができる.

テンプレートもまた関数と同じようデフォルト引数を持つ.これをデフォルトテンプレート引数と呼ぶ.テンプレート引数にオブジェクトやテンプレート引数を指定した場合,関数のデフォルト引数と同じである.もし型を指定した場合,そのデフォルト引数として型を指定できる.

テンプレートの特殊化

特定のテンプレート引数を与えられた時に,特殊な振る舞いを設定できる.このためのテンプレートの代替定義をユーザ定義の特別バージョンテンプレートの特殊化と呼ぶ.


template <class T> class Hoge
{
public:
  Hoge(){ std::cout << "etc\n"; }
};

template <> class Hoge<int>
{
public:
  Hoge(){ std::cout << "int\n"; }
};

int main()
{
  Hoge<char> c;		// etc
  Hoge<double> d;	// etc
  Hoge<int> i;		// int

  return 0;
}
テンプレートの全ての特別バージョンは,テンプレート本体と同じスコープで定義されていなければならない.

汎用テンプレート

特殊化されてないテンプレートを汎用テンプレートと呼ぶ.汎用テンプレートは,特別バージョンよりも前に宣言か定義されてなければならない.宣言の場合,どこかで汎用テンプレートが定義されている必要がある.

テンプレートの部分特殊化

テンプレートの特殊化では,型の一部を特定するような代替定義を行うことができる.これらの代替定義をテンプレートの部分特別バージョンテンプレートの部分特殊化と呼ぶ.


template <class T> class Hoge<T*>
{
public:
  Hoge(){ std::cout << "pointer\n"; }
};

int main()
{
  Hoge<char> c;		// etc
  Hoge<char*> cp;	// pointer
  Hoge<double> d;	// etc
  Hoge<int*> i;		// pointer

  return 0;
}

特別バージョンの優先順位

ある特別パターンに一致する全ての引数リストが,もう1つの特別パターンに一致するのに,逆は成り立たない場合,後者は前者より専門的である,という.例えば,特別バージョンのテンプレートは,汎用テンプレートよりも専門的である.コンパイラが曖昧性を解消する際,より専門的なテンプレートが選ばれる.

関数テンプレート

関数テンプレートの定義例を以下に示す.


template<class T1, class T2> T1 func(T2 t){ return u; }
多くのプログラマは,STL(Standard Template Library)で提供されるコンテナを使った関数を作る際,関数テンプレートを使用する.

関数テンプレートの引数

関数テンプレートの引数は,省略可能であるが,言語処理系が曖昧性を解消できない場合や適切な処理を行えない場合にエラーとなる.


int i = 10;
func(i);		// エラー:曖昧
func<int>(i);		// OK: 返り値を特定できる
func<int, int>(i);	// OK: 返り値を明示的に指定
func<int*, int>(i);	// エラー: intからint*への変換が無効
構文に曖昧性を持ち込む.P919

関数テンプレートの多重定義

C++では,同じ名前を持つ関数テンプレートや関数を定義できる.以下にそれらの曖昧性の解消に関するルールを示す.

基本的な規則
  • そのスコープに属する関数テンプレートと関数が候補
  • 関数テンプレートの引数には,格上げ,標準変換,ユーザ定義変換を適用することはできない.
曖昧性の解消
  1. スコープによって選ばれた関数テンプレートの中から最も専門的なものが選ばれる.
  2. 1.に通常の関数を加えた候補の中から,関数の多重定義の曖昧性を解消する規則に従って候補を絞る.
  3. 関数と関数テンプレートの一致の度合いが同じなら,関数が優先される.
  4. 一致するものがないか,複数ある時はエラーとなる.

派生とテンプレート

P406- 13.6 継承

テンプレートの変換

テンプレート引数としてのテンプレート

イテレータ(反復子)

イテレータとは,データ集合とアルゴリズムを結びつけるもののことである.データ集合に対するいくつかのアルゴリズムは,汎用的であり,データの性質に依存しないしかしイテレータのない言語では,プログラマは,データ構造にアクセスするためのインタフェースを用意し,そのインタフェースに従ったアルゴリズムを記述しなければならない.C++では,イテレータはデータ集合に対して標準的なアクセスモデルを提供する.そのためプログラマはそれにアクセスするための演算子や関数を特別に用意する必要はない.そしてプログラマに対し,データ構造に関係なくアルゴリズムを書けるようにする.

入力出力前方双方向ランダムアクセス
読み込み=*p=*p=*p=*p
アクセス->->->->[]
書き込み*p=*p=*p=*p=
反復++++++++++
--
+
---
+=
-=
比較==========
!=
<
!=!=!=!=>
>=
<=

export

exportはC++テンプレートの定義と宣言を分離するのを許可するよう指示する.ただしいくつかのコンパイラ(例えばgcc3.3以前やVisual C++など)は,exportがC++の正式な仕様であるにも関わらずサポートしていない.また,C++11ではexportは廃止となった。

その他

クラステンプレート名を多重定義することはできない.


template<class C> Hoge {};
class Hoge {};			 //NG

目録

キーワード

-緑の文字をクリック-