Boost.GeometryのFusionアダプトを作ってるときに嵌った問題。
Fusionシーケンスとしてアダプトされた全ての型をGeometryのConceptとして扱えるようにするために、いくつかのメタ関数をFusionシーケンスで特殊化する必要がありました。しかし残念ながら、部分特殊化には制限がありました。
以下のように、特殊化の構文でenable_ifを使ってテンプレートパラメータの型がFusionシーケンスかどうかを判断し、enable_ifの結果の型で特殊化する、という書き方はできません。
#include <boost/fusion/support/is_sequence.hpp> #include <boost/fusion/include/vector.hpp> #include <boost/utility/enable_if.hpp> template <class T> struct X { static const int value = false; }; template <class Seq> struct X< typename boost::enable_if< boost::fusion::traits::is_sequence<Seq>, Seq >::type // 戻り値の型はSeq > { static const int value = true; }; int main() { static_assert(X<boost::fusion::vector<int, char> >::value, "not fusion sequence"); }
main.cpp:11:8: error: template parameters not used in partial specialization: cpp/main.cpp:11:8: error: 'Seq'
テンプレートパラメータが部分特殊化で使われていない、というふうに言われます。
Xのプライマリテンプレートが使えればこの問題は容易に解決できるのですが、そんなことをするとFusionシーケンスがデフォルトでGeometryのConceptになるという謎の仕様になってしまいます。
そこで回避策として、メタ関数にenable_if用のダミーパラメータを持たせる、という方法をとりました。
#include <boost/fusion/support/is_sequence.hpp> #include <boost/fusion/include/vector.hpp> #include <boost/utility/enable_if.hpp> // Enableはenable_if用ダミーパラメータ template <class T, class Enable = void> struct X { static const int value = false; }; template <class Seq> struct X< Seq, typename boost::enable_if< boost::fusion::traits::is_sequence<Seq> >::type // 戻り値の型はvoid > { static const int value = true; }; int main() { static_assert(X<boost::fusion::vector<int, char> >::value, "not fusion sequence"); }
これでメタ関数Xが、Fusionシーケンスかどうかで機能を分岐できるようになりました。
これを実際に適用した例は、Boost 1.47.0以降のboost/geometry/geometries/adapted/boost_fusion.hppで見ることができます。
これは、今後コンセプトベースのC++ライブラリが現れていくにつれ、よく遭遇する問題となってくるでしょうから、覚えておくと便利です。