>すべてのETライブラリが上のように引数の型を限定していないと、同時に複数のETライブラリを使おうとしたときODR違反になってしまいます。
なので,従来CRTPで行われていた引数の型に対する制限(オーバーロードの導入の制限)をSFINAEで書き換えられるんじゃないかなぁ,と思いついたわけです.
具体的なコードとしては,例えば以下のような感じです.
#include <boost/utility/enable_if.hpp> #include <boost/mpl/and.hpp> #include <boost/type_traits/is_base_and_derived.hpp> // ETライブラリその1 "hoge" (CRTPを用いていないことに注意) // hogeにのみoverload resolution setを制限するためのタグ struct hoge_overload_restrictor{ }; // hoge用加算式テンプレートクラス template<class LHS, class RHS> class hoge_plus : public hoge_overload_restrictor { public: hoge_plus(LHS const &lhs, RHS const &rhs) : lhs_(lhs) , rhs_(rhs) { } private: LHS const &lhs_; RHS const &rhs_; }; // hogeに属するクラスその1 class hoge1 : public hoge_overload_restrictor { }; // hogeに属するクラスその2 class hoge2 : public hoge_overload_restrictor { }; // 演算子のオーバーロードをhogeに制限するためのSFINAEを提供するメタ関数. // 関数の戻り値の型に使用する. // 意味:LHS, RHS双方がhoge_overload_restrictorを継承している場合にのみ, // その関数テンプレートをoverload resolution setに導入する. // それ以外の場合,戻り値が不定(typeが定義されない)なので // その関数テンプレートはSFINAEルールに従って // overload resolution setから除外される. template<class LHS, class RHS, class ResultType> struct hoge_ol_restriction : public boost::enable_if< boost::mpl::and_< boost::is_base_and_derived<hoge_overload_restrictor, LHS>, boost::is_base_and_derived<hoge_overload_restrictor, RHS> >, ResultType > { }; // hogeからしか見えないoperator+ template<class LHS, class RHS> inline typename hoge_ol_restriction<LHS, RHS, hoge_plus<LHS, RHS> >::type operator+(LHS const &lhs, RHS const &rhs) { return hoge_plus<LHS, RHS>(lhs, rhs); } // ETライブラリその2 "huga" struct huga_overload_restrictor{ }; template<class LHS, class RHS> class huga_plus : public huga_overload_restrictor { public: huga_plus(LHS const &lhs, RHS const &rhs) : lhs_(lhs) , rhs_(rhs) { } private: LHS const &lhs_; RHS const &rhs_; }; class huga1 : public huga_overload_restrictor { }; class huga2 : public huga_overload_restrictor { }; // 先と同様 template<class LHS, class RHS, class ResultType> struct huga_ol_restriction : public boost::enable_if< boost::mpl::and_< boost::is_base_and_derived<huga_overload_restrictor, LHS>, boost::is_base_and_derived<huga_overload_restrictor, RHS> >, ResultType > { }; // hugaからしか見えないoperator+ template<class LHS, class RHS> inline typename huga_ol_restriction<LHS, RHS, huga_plus<LHS, RHS> >::type operator+(LHS const &lhs, RHS const &rhs) { return huga_plus<LHS, RHS>(lhs, rhs); } int main() { hoge1 a; hoge2 b; a + b + b; // hoge用のoperator+しか見えていない huga1 s; huga2 t; s + t + s; // huga用のoperator+しか見えていない }
SFINAEを使って汎用関数のオーバーロードを制限する方法にはいくつかあると思うのですが,ETにおける演算子のオーバーロードを限定するという用途では,この「識別用のタグを継承する + boost::is_base_and_derivedで判定する」というのがいくつかの理由から最善だと思われたのでこうしてみました.上のコードでは戻り値の型にenable_if使っていますが,別に戻り値じゃなくても全然良かったというか,引数の型にenable_if使ったほうがより柔軟な運用が出来る場面も多い気がします.
この手法の利点は演算子のオーバーロードの制限に関する実装とETの本来の実装との分離度が高くなる点でしょうか.この手法を利用するならETにおけるCRTPの存在意義はほとんど無くなるような気がします.
最大の欠点はSFINAEのポータビリティがまだ十分でないことで,かなり新しいコンパイラじゃないと通らないことだと思います.それでも最近はSFINAEを扱えるコンパイラがだいぶ普及してきましたが.後はオーバーロード制限のための実装がTMP特有のものになることでしょうか.こちらも本が出たことですしそのうち普及してくると思いたいw.
named_paramsあたりの実装見てSFINAEによる他のオーバーロード制限の方法がないか確認したいんですが時間がにゃい・・・.