C++ コンパイル時に値の型を知る方法

C++03以前にはそんなものはない

C++03以前には値の型を得ることのできるような仕組みは存在しません

それでも事前に登録しておいた型だけで構わないという限定的な条件の下でなら、似たような機能を実現することもできます

BOOST_TYPEOF

boost には BOOST_TYPEOF があります
しかし BOOST_TYPEOF は大掛かり過ぎるのでまずは簡単な方法で実装してみましょう

簡単な実装

まず値から直接的に型を得るようなことはできないので、template argument deduction を使って値から型に関連付けられている id を得ることにします
そしてこの id から関連付けられている型を得ることで機能を実現します

struct typeof_helper_t {
	struct type1 { char unused[1]; };
	struct type2 { type1 unused[2]; };
	typedef type1	typeid_bool;
	typedef type2	typeid_int;

	static typeid_bool	get_typeid( bool );
	static typeid_int	get_typeid( int );
};
template<size_t> struct typeof_t;
template<> struct typeof_t<sizeof(typeof_helper_t::typeid_bool)> { typedef bool	type; };
template<> struct typeof_t<sizeof(typeof_helper_t::typeid_int)> { typedef int	type; };
#define TYPEOF( N )	typeof_t<sizeof(typeof_helper_t::get_typeid(N))>::type

template argument deduction については「C++ 配列の要素数を静的に取得する方法」に譲ります

拡張可能な簡単な実装

上記の簡単な実装では型を登録するのにいちいち typeof_helper_t と typeof_t 編集しないといけなくて面倒でした
typeof_helper_t を適当なテンプレートに分けて、特殊化部分をマクロにすると簡単に拡張できそうです

template<size_t id> struct typeof_t;
template<typename T> struct typeid_t;
template<size_t id> struct typeid_helper_t {
	char unused[id];
};
struct typeof_helper_t {
	template<typename T> static typeid_helper_t<typeid_t<T>::value>	get_typeid(T);
};
#define TYPEOF( N ) typeof_t<sizeof(typeof_helper_t::get_typeid(N))>::type

#define DEFINE_TYPEID( T, id ) \
template<> struct typeid_t<T> { static const size_t	value = id; }; \
template<> struct typeof_t<id> { typedef T	type; };

DEFINE_TYPEID( bool, 1 )
DEFINE_TYPEID( int, 2 )
DEFINE_TYPEID( const char*, 3 )
DEFINE_TYPEID( const wchar_t*, 4 )

使用例その1

例えば STATIC_PRINT マクロをこのようにすると int 以外の値も表示できるようになります

template<typename T,T N,bool = false> struct static_print_t {
	static void	print(){
		return	static_print_t<T,N,true>::print();
	}
};
template<typename T,T N> struct static_print_t<T,N,true> {};
#define STATIC_PRINT( N ) static_print_t<TYPEOF(N),N>::print()

STATIC_PRINT( true );
STATIC_PRINT( 1 );

使用例その2

例えば次のようにすると、値から型を推論してくれるようになって変数の型を明示的に指定する必要がなくなったりもします

#define AUTO( name, value ) TYPEOF(value) name = value

int	main(){
	AUTO(aaa,"aaa");
	AUTO(b,true);
	AUTO(n,100);
	debug_printf( "aaa=\"%s\",b=%d,n=%d\n", aaa, b, n );
	return	0;
}

BOOST_TYPEOF はいちいち型を登録しない

BOOST_TYPEOF はいちいち型を登録しなくても使えます
これは BOOST_TYPEOF の中で自動的に型の登録を行うようにしてあるからです

型の登録というのは上でもやっている型と番号の関連付けということですが、これを自動で行うためにはテンプレートで使えるコンパイル時カウンタが必要になってきます
boost の場合は encode_counter というテンプレートがそれです

興味があったら実装してみるのもおもしろいと思います

C++11以降では decltype

C++11以降では decltype としてこの機能が標準に取り入れられました