実行環境
wandbox
gcc 7.3.0
g++ prog.cc -Wall -Wextra -std=c++11
https://wandbox.org/permlink/yj0ruS6pPwGgwAFY
知りたいもの
継承関係を持ついくつかの構造体が定義されているとする.
struct A{
int x;
A(int _x):x(_x){ }
virtual ~A(){ }
};
struct B1: public A{
int y;
B1(int _x, int _y):A(_x), y(_y){ }
~B1(){ }
};
struct B2: public A{
int y;
B2(int _x, int _y):A(_x), y(_y){ }
~B2(){ }
};
~A()
に virtual
が付いているから,プログラマはきっと
子クラスを親クラスのポインタに入れて利用するはず.
unique_ptr<A> p(new B1(1, 2));
p
の中に A
が入っているのか, B1
が入っているのか,B2
が入っているのか知りたい.
解決策
typeid
typeid情報を使うと,実行時型情報を取り出すことが出来る.
*p
の型情報とB1
の型情報を取り出して比較すれば,先程の知りたい情報が得られる.
unique_ptr<A> p(new B1(1, 2));
if (typeid(*p) == typeid(A))
cout << "A" << endl;
if (typeid(*p) == typeid(B1))
cout << "B1" << endl;
if (typeid(*p) == typeid(B2))
cout << "B2" << endl;
実行すると B1
と表示されるはず.
関数化
毎回 typeid(*p) == typeid(B1)
と書くのも妙な気がしたので,テンプレートを使った関数にまとめた.
template<typename T, typename U>
inline bool typeis1(const U& a1) {
return typeid(a1) == typeid(T);
}
使い方は次のようになる.
unique_ptr<A> p(new B1(1, 2));
if (typeis1<B1>(*p))
cout << "B1" << endl;
if (typeis1<B2>(*p))
cout << "B2" << endl;
ケアレスミスの回避
特に自分はうっかりミスをしてしまいがちなので,次のようなコードを書いてしまう.
コンパイルエラーも起きないし,ただfalse
が返るだけなので,ランタイムエラーにもならない.
unique_ptr<A> p(new B1(1, 2));
// ポインタを渡している
if (typeis1<B1>(p))
cout << "typeis1<B1>(p)" << endl;
unique_ptr<string> px(new string());
// 無関係な型の値を渡している
if (typeis1<B1>(*px))
cout << "typeis1<B1>(*px)" << endl;
継承関係が成立していないならば,コンパイルエラーを吐き出すようにしたい.
これはメタ関数を使って実装できる.
template<typename Derived, typename Base,
typename enable_if<is_base_of<Base, Derived>::value>::type* = nullptr>
inline bool typeis2(Base& a1) {
return typeid(a1) == typeid(Derived);
}
使い方は次のようになる.
unique_ptr<A> p(new B1(1, 2));
unique_ptr<string> px(new string());
if (typeis2<B1>(*p))
cout << "typeis2<B1>(*p)" << endl;
// コメントアウトを外すとコンパイルエラー
// if (typeis2<B1>(p))
// cout << "typeis2<B1>(p)" << endl;
// if (typeis2<B1>(*px))
// cout << "typeis2<B1>(*px)" << endl;
おまけ
RTTI(実行時型情報)が利用できない場合(コンパイルフラグに-fno-rtti
があるなど)、
typeidがソースコードに含まれるとコンパイルエラーになります。