2012-04-26 13 views
5

SFINAEに問題があります。私は型に戻り値の型にかかわらず定義されたメンバ関数operator->があるかどうかを判断できる必要があります。例を次に示します。SFINAE - テンプレートの型に '変数'戻り値型のメンバ関数があるかどうかを確認しようとしています

このクラスはテスターに​​あります。戻り値の型がX *のoperator - >()が定義されています。したがって、どこにいても「X」がどこにハードコードするのか分かりません。

template <class X> 
class PointerX 
{ 
    ... 

    X* operator->() const; 
    ... 
} 

このクラスは、渡されたTにメソッドoperator-> definedがあるかどうかを判断しようと試みます。演算子 - >戻り値の型に関係なく。

template<typename T> 
struct HasOperatorMemberAccessor 
{ 
    template <typename R, typename C> static R GetReturnType(R (C::*)()const); 

    template<typename U, typename R, R(U::*)()const> struct SFINAE{}; 
    template<typename U> static char Test(SFINAE<U,  decltype(GetReturnType(&U::operator->)), &U::operator-> >*); 
    template<typename U> static uint Test(...); 
    static const bool value = sizeof(Test<T>(0)) == sizeof(char); 
}; 

このクラスはoperator->戻り型が「オブジェクト」でなければならないこと以外は上記と全く同じです。

template<typename T> 
struct HasOperatorMemberAccessorOBJECT 
{ 
    template <typename R, typename C> static R GetReturnType(R (C::*)()const); 

    template<typename U, typename R, R(U::*)()const> struct SFINAE{}; 
    template<typename U> static char Test(SFINAE<U,  Object*,    &U::operator-> >*); // only change is we hardcoded Object as return type. 
    template<typename U> static uint Test(...); 
    static const bool value = sizeof(Test<T>(0)) == sizeof(char); 
}; 

結果:

void main() 
{ 
    HasOperatorMemberAccessor<PointerX<Object>>::Test<PointerX<Object>>(0);   // fails ::value is false; Test => Test(...) 

    HasOperatorMemberAccessorOBJECT<PointerX<Object>>::Test<PointerX<Object>>(0);  // works! ::value is true; Test => Test(SFINAE<>*) 
} 

HasOperatorMemberAccessorはPointXのメンバ関数 " - >()constオブジェクト演算子" を見つけることができませんでした。 したがって、Testの汎用バージョンTest(...)が使用されます。

ただし、HasOperatorMemberAccessorOBJECTはPointXの "Object operator - >(const)"を検索できました。 したがって、テスト専用バージョンテスト(SFINAE *)が使用されます。

どちらも "Object operator - >()const"メソッドを見つけることができているはずです。 TestのSpecialized Test(SFINAE *)を使用する必要があります。したがって、HasOperatorMemberAccessor :: :: valueは両方に対してtrueになるはずです。

HasOperatorMemberAccessorとHasOperatorMemberAccessorOBJECT間の唯一の違いはHasOperatorMemberAccessorOBJECTオブジェクトにハードコード型名Rを有していることである、

だから、問題は "decltype(GetReturnType(& U :: operator->は))" オブジェクトを返していないことです正しく私は戻り値の型を発見するためにいくつかの異なる許可を試しました。それらは次のようになります:

decltype(GetReturnType(&U::operator->)) 
    typename decltype(GetReturnType(&U::operator->)) 
    decltype(((U*)nullptr)->operator->()) 
    typename decltype(((U*)nullptr)->operator->()) 

何もありません、なぜですか?私はMSVC++ 10.0を使用しています。

+0

目立つものは、 'PointerX :: operator->'が 'bool * 'ではなく' bool * 'を返すということです。 –

+0

PointerXのXのタイプは、HasOperatorMemberAccessorに関する限り重要ではありません。この例に多くの無関係のオブジェクトを追加しないことで、私の問題を一般化しようとしました。あまりにも混乱している場合、私はboolを文字列に変更します。 –

+0

私にもう一度お試しください。 'PointerX :: operator->'は 'Object *'を返します。 'decltype(...)'は 'Object *'になります。 'HasOperatorMemberAccessorOBJECT'は' decltype(...) 'を' Object'に置き換えても、一見成功します。ここで何かが間違っているように見えます。私は 'HasOperatorMemberAccessor'について全く話していません。 –

答えて

4

このような特性を実装する方法、またはdecltypeが期待どおりに動作しないのはなぜですか?かつての場合は、ここでの一つのアプローチがあります:

#include <type_traits> 

template<typename T, bool DisableB = std::is_fundamental<T>::value> 
struct HasOperatorMemberAccessor 
{ 
private: 
    typedef char no; 
    struct yes { no m[2]; }; 

    struct ambiguator { char* operator ->() { return nullptr; } }; 
    struct combined : T, ambiguator { }; 
    static combined* make(); 

    template<typename U, U> struct check_impl; 
    template<typename U> 
    static no check(
     U*, 
     check_impl<char* (ambiguator::*)(), &U::operator ->>* = nullptr 
    ); 
    static yes check(...); 

public: 
    static bool const value=std::is_same<decltype(check(make())), yes>::value; 
}; 

// false for fundamental types, else the definition of combined will fail 
template<typename T> 
struct HasOperatorMemberAccessor<T, true> : std::false_type { }; 

// true for non-void pointers 
template<typename T> 
struct HasOperatorMemberAccessor<T*, false> : 
    std::integral_constant< 
     bool, 
     !std::is_same<typename std::remove_cv<T>::type, void>::value 
    > 
{ }; 

template<typename X> 
struct PointerX 
{ 
    X* operator ->() const { return nullptr; } 
}; 

struct X { }; 

int main() 
{ 
    static_assert(
     HasOperatorMemberAccessor<PointerX<bool>>::value, 
     "PointerX<> has operator->" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<X>::value, 
     "X has no operator->" 
    ); 
    static_assert(
     HasOperatorMemberAccessor<int*>::value, 
     "int* is dereferencable" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<int>::value, 
     "int is not dereferencable" 
    ); 
    static_assert(
     !HasOperatorMemberAccessor<void*>::value, 
     "void* is not dereferencable" 
    ); 
} 

VC++ 2010は、必要に応じてC++ 11件の施設(例えば表現SFINAE)が、これは非常にクリーンにするために必要な欠けています。

+0

これは、Tがポインタ型であることを除いて、ほとんど私のために働いていました。私は基底を次のように変更しました: struct base:条件式<!is_pointer :: value、T、no> :: type、base_mixin {}; 私はdecltypeがうまくいかなかった理由を知りたいと思います。 –

+0

@MichaelG: 'HasOperatorMemberAccessor <>'はポインタのために特殊化することができます: 'template class HasOperatorMemberAccessor :public std :: true_type {};'。 'decltype'が期待通りに動作しない場合、私はあなたのコードを詳しく調べていませんでしたが、VC++ 2010の' decltype'実装はそれに先立ってバグがあります。 – ildjarn

+0

@Michael:適切なポインタ型の検出で編集されました( 'void *'はちょっとした問題でした)。 – ildjarn

関連する問題