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 件のコメント:
コメントを投稿