今日の話題はSFINAE(Substitution Failure Is Not An Error),およびSFINAEを活用する基礎ツールとなるboost::enable_ifについての話題です.以下ではSFINAEについての基本的な知識,およびboost::enable_ifについての知識を仮定します.
SFINAEについてはK.INABA氏の説明が詳しいのでそちらを参照してください.また,boost::enable_ifについては同じくK.INABA氏の『Boost C++ Libraryプログラミング』(asin:4798007862)や,公式ドキュメント(残念ながら日本語訳が見当たりません)を参照してください.
さて,まずは以下のようなクラスを考えます.
templateclass C{ public: C(T const &val = T()) : v(val){} C(C const &r) : v(r.v){} private: T v; };
さて,このclass Cに対して次のコードを試してみます.
Ca; C b(a); // コンパイルエラー!!
上のコードを書いた人の主張は恐らくこうでしょう.
「charはintへの暗黙の変換が効くのだから,C
しかし,上のコードは通りません.C
C
しかし一方でCのようなクラスに対して,上のような暗黙の型変換を提供したいこともあるでしょう.その場合,以下のようにコンストラクタのテンプレートを定義すれば良いことになります.
templateclass C{ template friend class C; public: C(T const &val = T()) : v(val){} C(C const &r) : v(r.v){} template C(C const &r) : v(r.v){} private: T v; };
3つ目のコンストラクタがC
たとえば
Ca; C b(a);
と書けば,2行目のコードは3つのコンストラクタテンプレートにおいてOtherT = charとして実体化されたものを呼び出していることになります.
上のコードで一点だけ注意するべきことがあります.以下の部分です.
templatefriend class C;
これは"friend template(宣言)"と呼ばれるもので,意味としては「あらゆるtemplate引数Tに対して,class
なぜこれが必要かというと,以下のコード
templateC(C const &r) : v(r.v){} // C のprivateメンバvに直接アクセスしている
において,C
何度も言うようですがC
上のような簡単な例では,vに対する公開メンバ関数からアクセスすれば良いでしょうが,一般には上のようにfriend templateを宣言したほうが良い場合も多いでしょう.
ちなみに,上のコードに対して下のコードは当然通りません.
Ca; C b(a);
char*からintへの暗黙の型変換は許されていないため,コンストラクタテンプレートのv(r.v)という初期化子の記述の部分でエラーを引き起こすからです.