型特性によるオーバーロードはいくつか方法がありますが、目的に合わせた方法をとるのが良いと思います。
・何通りかの型特性でオーバーロードをしたい場合
・ある型特性かどうかでオーバーロードしたい場合
・ある型特性でしかオーバーロードしてほしくない場合
の3つの場合に分けて考えてみます。
何通りかの型特性でオーバーロードしたい場合
普通にenable_ifを使ってオーバーロードをする。
例
#include <type_traits> #include <iostream> template <class T> void test(T, typename std::enable_if<std::is_class<T>::value>::type* = nullptr) { std::cout << "Tはクラス\n"; } template <class T> void test(T, typename std::enable_if<!std::is_class<T>::value>::type* = nullptr) { std::cout << "Tはクラスではない\n"; } struct hoge{}; int main() { test(3); test(hoge()); }
実行結果
Tはクラスではない Tはクラス
適宜enable_ifのテンプレート引数を型特性メタ関数に置き換えれば良いと思います。
関数の引数ではなく、テンプレート引数でオーバーロードする方法もあります。
#include <type_traits> #include <iostream> extern void* enabler; template <class T, typename std::enable_if<std::is_class<T>::value>::type*& = enabler> void test(T) { std::cout << "Tはクラス\n"; } template <class T, typename std::enable_if<!std::is_class<T>::value>::type*& = enabler> void test(T) { std::cout << "Tはクラスではない\n"; } struct hoge{}; int main() { test(3); test(hoge()); }
実行結果
Tはクラスではない Tはクラス
詳しくは本の虫: C++0xにおけるenable_ifの新しい使い方を見て下さい。
1つめの例は引数の数が決まっている演算子のオーバーロードやコンストラクタには使いづらいものですけど、2つめは問題なく使えるという差があります。
ちなみに2つめの例はVC++11.0ではC2668エラーでコンパイル通りませんでした。g++4.6.2では問題なく動きました。
2013/7/1追記
VC++12.0(PR)ではちゃんと動きました。
ある型特性かどうかでオーバーロードしたい場合
つまり全部で2通り(その型特性か否か)でオーバーロードをしたい場合は、タグ・ディスパッチによるオーバーロードが良いと思います。
#include <type_traits> #include <iostream> template <class T> void test_impl(T, std::true_type) { std::cout << "Tはクラス\n"; } template <class T> void test_impl(T, std::false_type) { std::cout << "Tはクラスではない\n"; } template <class T> void test(T t) { test_impl(t, std::is_class<T>()); } struct hoge{}; int main() { test(3); test(hoge()); }
実行結果
Tはクラスではない Tはクラス
標準の型特性メタ関数はtrue_typeかfalse_typeを継承しているのでこのようなことが行えます。Tがポインタ型かどうかぐらいならis_pointerを使うまでもなく、普通に部分特殊化すれば良いと思います。
ある型特性でしかオーバーロードしてほしくない場合
ある型特性を満たしたTしか適用したくない場合、enable_ifを用いたオーバーロードだとエラー文がわかりづらくなります。
例
#include <type_traits> template <class T> void test(T, typename std::enable_if<std::is_class<T>::value>::type* = nullptr) { } struct hoge{}; int main() { test(hoge());//OK test(3);//ERROR }
エラー文(VC++11.0)
エラー 1 error C2893: Failed to specialize function template 'void test(T,std::enable_if<std::is_class<_Ty>::value,void>::type *)' 2 IntelliSense: 関数テンプレート "test" のインスタンスが引数リストと一致しません 引数の型: (int)
このような場合はstatic_assertを使うとエラー文が分かりやすくなります。
例
#include <type_traits> template <class T> void test(T) { static_assert(std::is_class<T>::value, "T must be class"); } struct hoge{}; int main() { test(hoge());//OK test(3);//ERROR }
エラー文(VC++11.0)
エラー 1 error C2338: T must be class
参考
http://cpplover.blogspot.jp/2011/04/c0xenableif.html
http://flamingdangerzone.com/cxx11/2013/02/11/to-sfinae-or-not-to-sfinae