5

SFINAEに問題があります。タイプに、その戻りタイプに関係なく、メンバー関数operator->が定義されているかどうかを判別できる必要があります。次に例を示します。

テスターのこのクラス。X *の戻り型でoperator->()を定義します。したがって、どこでもハードコーディングするための「X」が何であるかはわかりません。

template <class X>
class PointerX
{
    ...

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

このクラスは、渡されたTにメソッドoperator->が定義されているかどうかを判別しようとします。演算子->戻り値のタイプに関係なく。

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-> returntypeが'Object'でなければならないことを除いて、上記とまったく同じです。

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のメンバー関数「Objectoperator->()const」を見つけることができませんでした。したがって、Testの汎用バージョンTest(...)を使用します。

ただし、HasOperatorMemberAccessorOBJECTは、PointXの「オブジェクト演算子->()定数」を見つけることができました。したがって、Test専用バージョンTest(SFINAE *)を使用します。

どちらも「Objectoperator->()const」メソッドを見つけることができたはずです。したがって、両方ともTestの専用バージョン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を使用しています。

4

1 に答える 1

4

そのような特性を実装する方法を尋ねていますか、それともなぜdecltype期待どおりに動作しないのですか?前者の場合、次の1つのアプローチがあります。

#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など)がありません。

于 2012-04-26T20:30:05.287 に答える