プログラミングの魔物

エラー、バグ、仕様変更と戦うブログ

C++テンプレート完全ガイドの復習

内容的にはC++テンプレートテクニックと被ってる部分が多く、こちらのほうがより詳細に解説している。ただし、洋書の翻訳版なので表現がわかりにくい部分があったり、正誤表に載っていない表記ミス(アンダーバーがない等)があったり・・・。読みやすさではC++テンプレートテクニックのほうが上。

テンプレート引数のキーワードにclassを使うべきかtypenameを使うべきか

テンプレートを書く時classまたはtypenameによってテンプレート引数を宣言できる

template<class T> void foo(T);
template<typename T> void foo(T);

C++テンプレート完全ガイドではtypenameを使うことが推奨されている。
理由はTにはclass以外の型も代入できるからということらしい。

3.3クラステンプレートの特殊化

template<>
class Stack<std::string>{
...
};

3.4部分的な特殊化

template<typename T1, typename T2>
class MyClass{
...
};
//
template<typename T>
class MyClass<T, T>{
...
};
template<typename T>
class MyClass<T, int>{
...
};
template<typename T1, typename T2>
class MyClass<T1*, T2*>{
...
};

あいまいになるとエラー

MyClass<int,int> m;  //MyClass<T, T>とMyClass<T, int>の両方に合致

4.3ノンタイプテンプレートパラメータの制限

ノンタイプテンプレートパラメータには整数値(列挙子を含む)か外部リンケージのオブジェクトへのポインタが使える。
浮動小数点数とクラス型のオブジェクトはノンタイプテンプレートパラメータに使えない。

5.4テンプレートテンプレートパラメータ

テンプレート引数にテンプレートを取る。

template <template <typename ELEM, typename = std::allocator<ELEM> > class CONT = std::deque>

※CONTはテンプレートテンプレートパラメータ

5.6関数テンプレートの引数に文字列リテラルを使う

文字列リテラルはchar const[n]なので文字数の異なるリテラルは別の型になってしまう。

6章

テンプレートは宣言と定義を分けると面倒だからすべてをヘッダに記述する包含モデルで作ったほうが良い。他には明示的なインスタンス化と分割モデルがある。
明示的なインスタンス化を行うにはプログラム中でテンプレートがどの型でインスタンス化を行なっているかを知っている必要があるので面倒。分割モデルは対応しているコンパイラが少ないかも。

6.5プリコンパイル済みヘッダ

大きいヘッダを扱うときにはプリコンパイル済みヘッダを使うとコンパイル時間短縮。

6.6テンプレートのデバッグ

C++0xの機能を使うとこの辺も楽になるんじゃないだろうか。

7.1クラステンプレートかテンプレートクラスか?

クラステンプレート・・・クラスがテンプレートであることを表す。
テンプレートクラス・・・あいまい
関数テンプレートやメンバ関数テンプレートと合わせてクラスはクラステンプレートと呼ぼう

7.4ODR

定義は一度のルール
非インラインの関数とメンバ関数は、グローバル変数と静的データメンバと同じく、プログラム全体を通して一度だけ定義されねばならない
クラス型とインライン関数は翻訳単位において1回だけ定義されねばならない。かつ、それらの定義はプログラム全体の中で同一でなければならない。

テンプレート引数とテンプレートパラメータ

テンプレート名とそれに続く角括弧中の引数の組み合わせをテンプレートIDという。
テンプレートパラメータ・・・宣言もしくは定義中でtemplateキーワードのあとに列挙される名前
テンプレート引数・・・テンプレートパラメータに代入される項

8.1宣言のパラメータ化

template <typename T>
class List{  //名前空間スコープのクラステンプレート
  public:
  template <typename T2>  //メンバ関数テンプレート
  List (List<T2> const&);  //(コンストラクタ)
};
template <typename T>
  template <typename T2>
List<T>::List (List<T2> const& b){  //メンバ関数テンプレートをクラス外で定義
}

8.1.1仮想メンバ関数

メンバ関数テンプレートを仮想であると宣言することはできない。

8.2テンプレートパラメータ

宣言においてあとで使用しないパラメータの名前は記述しなくてもいい。

8.2.2ノンタイプパラメータ

整数もしくは列挙型
ポインタ型
参照型

8.4.1フレンド関数

関数テンプレートのインスタンスをfriendで定義できない
テンプレート引数に依存しないフレンド関数は複数のインスタンスをするとエラーになる(クラステンプレートのフレンド関数はテンプレートに依存した形を取る必要がある)

8.42フレンドテンプレート

class Manager{
  template<typename T>
    friend class Task;
  template<typename T>
    friend void Schedule<T>::dispatch(Task<T>*);
  template<typename T>
    friend int ticket(){
      return ++Manager::counter;
    }
  static int counter;
};

普通のフレンド宣言と同じように名前が就職されておらず、角カッコがついていない時に限りフレンドテンプレートを定義していい。フレンドテンプレートの宣言はプライマリテンプレートとプライマリテンプレートのメンバだけ。特殊化は自動的にフレンドとなる。

9.2.1引数依存検索ADL

呼び出しの引数に関連する名前空間とクラスから適切なメソッドが検索される。ただしusing指令は無視される

参考:

C++ テンプレート完全ガイド (Programmer’s SELECTION)

C++ テンプレート完全ガイド (Programmer’s SELECTION)