読者です 読者をやめる 読者になる 読者になる

クラス内にあるtypedefが存在するか調べる

C++ プログラミング

あるクラスにあるtypedefが存在するか否かを調べる方法はないかな?
とか悩んでいてTwitterでつぶやいたらフォロワーさんがそれSFINAEでできるよと教えてくれた。

SFINAEとは「Substitution Failure Is Not An Error」のことで「置き換えの失敗はエラーにならない」というものらしい。
これは満たしているかどうか調べたい条件のパターンマッチを書いておいて、仮にそれを満たさない(今回の場合はtypedefが存在しない)場合は別のパターンマッチが試され...ってのが繰り返されパターンマッチのいずれかに適合しさえすれば(たとえそのパターンマッチのいくつか単体では不正であったとしても)
コンパイルエラーにならないということ。(もちろんすべてを満たさない場合はコンパイルエラーとなる)
つまり「ある型が条件を満たすかどうか」の判断に使える。

今回の目的のために使ったもの(を若干改造した)サンプルは以下のとおり。
これではParameterというものがクラス内でtypedefして定義されているか
を調べるというものだ。

#include<iostream>


struct yes{};
struct no{};

//定義されているかどうかを調べるためのクラス
template<typename TargetClass>
class definedParameterTypedef{
public:


private:

	//ここがキモ
	template <typename T>
		static yes check(typename T::Parameter*);
	template <typename>
	static no check(...);


public:
	typedef decltype(check<TargetClass>(nullptr)) Result;


};

//デバッグ
template<typename T>
void printType(){
	std::cout<<"Other"<<std::endl;
}

template<>
void printType<yes>(){
	std::cout<<"yes"<<std::endl;
}

template<>
void printType<no>(){
	std::cout<<"no"<<std::endl;
}


struct Hoge{
	typedef int Parameter;

};


int main(){

	printType<definedParameterTypedef<int>::Result>();
	printType<definedParameterTypedef<Hoge>::Result>();


	return 0;
}

テンプレートメンバ関数checkの引数によるパターンマッチによって
返り値の型が異なるメンバ関数が生成される。(...)のほうではどんなものでも受け取るため、パターンマッチがはずれることはない。また、なぜポインタにしているかということだが、こうすれば定数であるnullptrを渡しておけばコンパイル時に解決できるからだ。これがポインタでないとParameterとしてtypedefされているクラスのインスタンスを生成する必要があり、これはコンパイル時に行えるとは限らない。
問題はこの返り値の型をどうやって取り出すかということだが、C++11からはdecltypeがあるのでそれを使った。はじめてdecltypeが便利だなと思った瞬間。