元ネタ
仕事ではCばっかだし、最近C++ってあんまり触ってないなぁ。boost::call_traitsのソースを見たので横浜に向けてお勉強。
| #include <iostream> #include <typeinfo> #include <string> #include <vector> using namespace std; // // 渡された型のサイズで値と参照を切り替える // template<typename T, bool sw> struct type_switcher_helper { typedef const T type; }; template<typename T> struct type_switcher_helper<T, false> { typedef const T& type; }; template<typename T> struct type_switcher { typedef typename type_switcher_helper<T, sizeof(T) <= sizeof(void*)>::type type; }; // // 型名を取得する // 参照は値と判別できない名前を返すので特殊化する // template<typename T> struct get_type { static string name() { return string(typeid(T).name()); }}; template<typename T> struct get_type<T&> { static string name() { return string(typeid(T).name()) + string("&"); }}; // template<typename T> void print_type_name() { cout << get_type<T>::name() << endl; } // // ポインタ判定 // char is_pointer_helper(bool, const void* p); int is_pointer_helper(bool, ...); // 関数によるポインタ判定 template<typename T> bool is_pointer_func(T a) { return sizeof(is_pointer_helper(true, a)) == sizeof(char); } template<typename T> struct is_pointer_st { enum { Kind = 0 }; }; template<typename T> struct is_pointer_st<T*> { enum { Kind = 1 }; }; template<typename T> struct is_pointer_st<T* const> { enum { Kind = 1 }; }; int main() { cout << "--- Type name ---" << endl; print_type_name<int>(); print_type_name<int*>(); print_type_name<int&>(); print_type_name<const int*>(); print_type_name<volatile int*>(); print_type_name<vector<int> >(); // ここがcall_traits::param_typeと同じ事してるとこ print_type_name<type_switcher<int>::type >(); print_type_name<type_switcher<vector<int> >::type >(); int a = 0; int& b = a; cout << endl << "--- Type check by function ---" << endl; cout << (const char*) (is_pointer_func(a) ? "Pointer" : "Not pointer") << endl; cout << (const char*) (is_pointer_func(&a) ? "Pointer" : "Not pointer") << endl; cout << (const char*) (is_pointer_func(b) ? "Pointer" : "Not pointer") << endl; cout << (const char*) (is_pointer_func(&b) ? "Pointer" : "Not pointer") << endl; cout << endl << "--- Type check by struct ---" << endl; cout << (const char*) (is_pointer_st<int>::Kind ? "Pointer" : "Not pointer") << endl; cout << (const char*) (is_pointer_st<int*>::Kind ? "Pointer" : "Not pointer") << endl; cout << (const char*) (is_pointer_st<int&>::Kind ? "Pointer" : "Not pointer") << endl; return 0; } |
出力結果は以下のとおり。
VS2008
--- Type name ---
int
int *
int&
int const *
int volatile *
class std::vector<int,class std::allocator<int> >
int
class std::vector<int,class std::allocator<int> >&
--- Type check by function ---
Not pointer
Pointer
Not pointer
Pointer
--- Type check by struct ---
Not pointer
Pointer
Not pointer
Cygwin+GCC3.4.4
--- Type name ---
i
Pi
i&
PKi
PVi
St6vectorIiSaIiEE
i
St6vectorIiSaIiEE&
--- Type check by function ---
Not pointer
Pointer
Not pointer
Pointer
--- Type check by struct ---
Not pointer
Pointer
Not pointer
VS2008のtypeid().nameの名前に感動。
これだけだと問題があって、long longやdoubleのようなvoid*よりも大きいサイズの型だと組込型でも参照になっちゃう。
call_traitsのソースを見ると、その辺も考慮してもっときちんと書いてあるけど、これくらいのプログラムでも知りたいこと知れたからこれ以上追及はしない(ってかできないです)。