C/C++で配列を引数にとるには,ポインタで受ける方法と参照で受ける方法の2つがある.
なので,次のコードは関数のオーバーロードがあいまいという理由でコンパイル時にエラーになる.
#include <iostream> void f(int * p){ std::cout << "pointer version" << std::endl; } void f(int (&a)[2]){ std::cout << "array version" << std::endl; } int main(){ int * p; f(p); // ポインタ版が呼ばれる. int a[2]; f(a); // これがNG.オーバーロードがあいまい. }
これをテンプレート関数にしてみるとどうだろう?
普通に考えると同じくエラーになりそうなものだけど,g++ではコンパイルOKになる.
理由はよくわからない.
template<class T> void f(T * p){ std::cout << "pointer version" << std::endl; } template<class T,std::size_t N> void f(T (&a)[N]){ std::cout << "array version" << std::endl; } /* main関数は略 */
さらに,1つ目の関数の引数からポインタ記号を外して,"void f(T p)~"とすると(汎用型の関数になってしまうけど),vc++でもコンパイルできる.
「T=非ポインタ型」で成立するテンプレート関数と「T=ポインタ型」で成立するテンプレート関数がある場合は前者が優先されるようだ.
ということで,g++でもvc++でも通用するコードを書くとするなら,boost.type_traitsを利用するのがよさそうだ.
#include <iostream> #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_pointer.hpp> template<class T> typename boost::disable_if<boost::is_pointer<T>,void>::type f(const T & v){ std::cout << "general version" << std::endl; } template<class T> typename boost::enable_if<boost::is_pointer<T>,void>::type f(const T p){ std::cout << "pointer version" << std::endl; } template<class T,std::size_t N> void f(const T (&a)[N]){ std::cout << "array version" << std::endl; } int main(){ int v; f(v); // 汎用版 int * p; f(p); // ポインタ版 int a[2]; f(a); // 配列版 }
もちろん,部分特殊化+静的メンバ関数で同じことができるけど,こっちの方が読みやすい…ような気がする.
0 件のコメント:
コメントを投稿