もし有効ならば
こんにちは。プログラマのマツノブです。
今回もプログラムの話をしていきましょう。
今回はメタプログラミングでよく使うC++11で追加された std::enable_if を学んでいきましょう。
template< bool B, class T = void >
struct enable_if;
Bがtrueの場合enable_if::type は Tに
Bがfalseの場合enable_if::typeは未定義になります。
この特性を利用してGetData,getData,GET_DATAが混在する環境で実装されているメソッドを呼び出すコードについて考えてみましょう。
次のコードには前回の記事で紹介したマクロを使用します。
HAS_METHOD(GetData);
template<typename T>
std::enable_if<has_method_GetData<T>::value, void>::type Call_GetData(const T& obj){
obj.GetData();
}
HAS_METHOD(getData);
template<typename T>
std::enable_if<has_method_getData<T>::value, void>::type Call_GetData(const T& obj){
obj.getData();
}
HAS_METHOD(GET_DATA);
template<typename T>
std::enable_if<has_method_GET_DATA<T>::value, void>::type Call_GetData(const T& obj){
obj.GET_DATA();
}
class Hoge {
public:
void GetData(void){ }
};
class Piyo {
public:
void getData(void){ }
};
int main(){
Hoge h;
Piyo p;
Call_GetData(h); // h.GetData();
Call_GetData(p); // p.getData();
}
このコードでは has_method_xxx の value が true の時 enable_if の type はvoid になりますが false の場合未定義になります。
false の場合戻り値が未定義になりビルドエラーとなりますがSFINAEの作用でオーバーロードの候補から除外されるだけで次にめぼしいコードがないか探しに行きます。
これによって、Call_GetData関数は引数に渡された型Tが実装しているメソッドを正しく呼び出すことができるのです。
しかし、上記のコードではGetDataとgetDataの両方が実装されていたり一つも実装されていない場合にビルドエラーとなります。
一つも実装されていないことが許容される場合は
void Call_GetData(...){ }
を追加しておけばとりあえずビルドエラーにはなりません。
長々と書きましたがこのようなコードが必要な環境は命名規則に問題があるのでまずはそれを見なおしたほうがいいのではないでしょうか。
enable_ifにはこれ以外にも引数が文字列かそれ以外かで処理を分ける時などにも使えるのでいろいろな場面で有効なクラスです。
それでは皆さん創意工夫でより良いメタプログラミングを