私は一般的に、あなたが試みたようにカスタムenable_ifスタイルのタイプを作成することを好みます。なぜなら、コードは、の組み合わせではなく、単一のトレイトタイプで読む方が明確だからですenable_if<some_trait<T>, another_trait<T>>。ただし、この場合、コードにいくつかの問題があり、コードが機能しなくなります。
スペシャenable_if_has_barライゼーションが選択されることはありませんReturnBar。returntypeは、プライマリテンプレートをインスタンス化するだけenable_if_has_bar<foo, void>であり、ネストされたを定義することはありませんtype。スペシャライゼーションがインスタンス化される原因はないため、がT::bar有効な式であるかどうかをチェックするものはありません。
あなたのdecltype(static_cast<T*>(0)->*static_cast<decltype(&T::bar)>(0))表現はあなたがおそらく望むようにはint&ならないでしょう。intこれは、が左辺値とdecltype(foobar.*(&foo::bar))同等でdecltype(foobar.bar)あり、であるためです。したがって、はです。パラメータがconstであるために関数が返されると、関数のコンパイルに失敗するため、non-constにバインドすることはできません。foobar.bardecltypeint&ReturnBarint&valuevalue.barint&
動作するバージョンは次のとおりです。
template <class T>
class has_bar
{
template<typename U, typename = decltype(&U::bar)>
static std::true_type
test(U*);
static std::false_type
test(...);
public:
static const int value = decltype(test((T*)nullptr))::value;
};
template<typename T, bool = has_bar<T>::value>
struct enable_if_has_bar
{ };
template<typename T>
struct enable_if_has_bar<T, true>
: std::decay<decltype(std::declval<T&>().*(&T::bar))>
{ };
has_barこれは最初に、型にネストされたメンバーがあるかどうかの質問に答えるヘルパーを宣言します。そのヘルパーはSFINAEを使用してtrueまたはfalse値を取得します。&T::barが有効な式の場合、の最初のオーバーロードがtest使用されます。これは戻り値true_typeであるため、 ievalueに設定されます。それ以外の場合、フォールバックオーバーロードが選択され、に設定されます。true_type::valuetruevaluefalse
次に、enable_if_has_barテンプレートは、の値として推定されるデフォルトのテンプレートパラメータを使用しますhas_bar<T>::value。プライマリテンプレートは、has_bar<T>::valueがfalseの場合に使用されます。特殊化はhas_bar<T>::valuetrueの場合に使用されます。この場合、式&T::barが有効であることがわかり、それをdecltype式で使用して型を取得できます。
std::decayint&decltype式の結果をちょうどに変換するために使用されintます。fromの継承decayは、メンバーを定義するために使用するよりも少し短くなります。これは次のようになります。
typedef typename std::decay<decltype(std::declval<T&>().*(&T::bar))>::type type;
未評価の式で標準ユーティリティを使用しdeclval<T>()ました。これは、入力するのが少し短く、。よりも慣用的で表現力がありstatic_cast<T*>(0)ます。
使用する代わりに、タイプからタイプを取得decayするための別のヘルパータイプがあります。intint T::*
template<typename T>
struct remove_class;
{ };
template<typename Member, typename Class>
struct remove_class<Member Class::*>
{
typedef Member type;
};
template<typename T>
struct enable_if_has_bar<T, true>
: remove_class<decltype(&T::bar)>
{ };
(名前remove_classはあまり良くありませんが、基本的にはデータメンバーへのポインタ型を取り、メンバーの型を示します。)