テンプレート・テンプレート・パラメータ in Visual C++ 2010

 久しぶりに書きますね.

 本日はVisual C++ 2010(下位バージョンはしらない)において,
テンプレート・テンプレート・パラメータを使う場合に,
デフォルトパラメータを用いたいときに起こった問題,
という極端にマニアックな問題についてメモっておきます.


 その前に,本の宣伝をします.
/*begin ------------------------------ */

 以前ブログにコメント下さった方が本を書いているらしいので,購入してみました.
…というか,以前から目を付けていたものなので,ブログってすごいなと思った次第です.

C++テンプレートテクニック

C++テンプレートテクニック

 こいつぁすげえ本だ.こういうのを待っていた.
…激しくマニアックな気がするけれど.
テンプレート教団の末席信者としてはこのような教典を見つけられて非常にうれしい限りです.
 何だかんだ言ってテンプレートはすごく便利で,その技術をある程度網羅した本がほしいな,という気持ちの分かる人はこれを買うしかありません.
 解説も分かりやすく,例も豊富で久々の良書な気がします.

 ただすごい濃いので,僕には全てを一度に脳内インストール出来ません.
それでも全体をさらっておくと,

「こういう場合,テンプレートでなんかできたよな…」
 ↓
この本で調べる

という流れができるので,がんばって読んでみましょう.

 この辺の技術はコンパイラとの対話の確率が飛躍的に上がるので嫌になりますが,
「あー,テンプレートにしておいて良かった…」
と思える瞬間がきっとくるはず.

 皆さんテンプレートを大いに活用しましょう(布教活動)
/*end ------------------------------ */


 閑話休題

 そんなテンプレート教信者は今日もテンプレートをゴリゴリ書いていたわけなのですが,VC++2010でハマりました.

 テンプレートを使っていて,とても便利で離れられない機能「ポリシー」.
クラス内での処理をコンパイル時に切り替えるもの(C++テンプレートテクニックより)とのことですが,こんなページを見ている人には,説明よりもコードでしょう.

namespace nsp{

	class Def{
	public:
		void f(){ std::cout<<"default\n"; }
	};

	class Alt{
	public:
		void f(){ std::cout<<"alternative\n"; }
	};
	
	template<class Some_func = Default_func> 
	class A{
	public:
		Some_func sf;
		void g(){ sf.f(); }
	};
};

int main(){
	nsp::A<> a;
	a.g();
	
	return 0;
}

このコードの出力はdefaultとなります.
mainの中の1行目をnsp::A a;と書き換えれば,出力がalternativeとなります.
つまり,クラスの関数の処理を指定されたクラスに応じて切り替えることができます.すばらしい.

 ポリシーを使ったクラスは(僕は)結構使うし,慣れるとその内普通のクラス感覚で使うようになります(僕は).
そうすると,クラスをテンプレート引数としてとるようなテンプレートクラスを作るような場面にも遭遇すると思います.いや,僕が遭遇しました.

 ここで使うのが,いわゆるテンプレート・テンプレート・パラメータというやつですね.
先ほどのポリシーを使ったクラスをパラメータとしてとるテンプレートクラスを考えた場合,以下のようになると思います.

#include <iostream>

namespace nsp{

	class Def{
	public:
		void f(){ std::cout<<"default\n"; }
	};

	class Alt{
	public:
		void f(){ std::cout<<"alternative\n"; }
	};
	
	typedef Def Default_func;
	
	template<class Some_func = Default_func> 
	class A{
	public:
		Some_func sf;
		void g(){ sf.f(); }
	};
	
	
	template< template<class Some_func = Default_func>class T>
	class B{
	public:
		T<> Z;
		void h(){ Z.g(); }
	};
	
};

int main(){

	nsp::B<nsp::A> b;
	b.h();
	
	return 0;
}

Visual C++2010ではコンパイルが通りませんでした.何故だろう?

 問題は,テンプレート・テンプレート・パラメータの宣言で,

template< template<class Some_func = Default_func>class T>

 Visual C++ 2010では,宣言がnspスコープであるにもかかわらず,このDefault_funcのスコープを与えてやらなければいけないようです.

template< template<class Some_func = nsp::Default_func>class T>

g++ on cygwinではこの問題は起こりませんでした.
また,上のコードではデフォルトのクラスを使っていますが,

T<> Z;

デフォルトじゃない場合は問題が起こりませんでした.

T<Alt> Z;

 同じような問題にぶち当たる人はレアすぎると思うのですが,
まあ,自分用メモということで.