私は一般的に、あなたが試みたようにカスタム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.bar
decltype
int&
ReturnBar
int&
value
value.bar
int&
動作するバージョンは次のとおりです。
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::value
true
value
false
次に、enable_if_has_bar
テンプレートは、の値として推定されるデフォルトのテンプレートパラメータを使用しますhas_bar<T>::value
。プライマリテンプレートは、has_bar<T>::value
がfalseの場合に使用されます。特殊化はhas_bar<T>::value
trueの場合に使用されます。この場合、式&T::bar
が有効であることがわかり、それをdecltype式で使用して型を取得できます。
std::decay
int&
decltype式の結果をちょうどに変換するために使用されint
ます。fromの継承decay
は、メンバーを定義するために使用するよりも少し短くなります。これは次のようになります。
typedef typename std::decay<decltype(std::declval<T&>().*(&T::bar))>::type type;
未評価の式で標準ユーティリティを使用しdeclval<T>()
ました。これは、入力するのが少し短く、。よりも慣用的で表現力がありstatic_cast<T*>(0)
ます。
使用する代わりに、タイプからタイプを取得decay
するための別のヘルパータイプがあります。int
int 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
はあまり良くありませんが、基本的にはデータメンバーへのポインタ型を取り、メンバーの型を示します。)