SFINAE用于类型实例化

时间:2014-06-13 11:58:57

标签: c++ sfinae

我在C ++中遇到了Member Detector成语,这是一个类型特征,用于判断一个类是否包含某个名称的成员。但是如果类型不是类,链接的示例不能像我预期的那样工作:我想要任何非类类型的false结果。一个可能的解决方案肯定是这样的,使用boost::is_class<T>

template<typename T>
struct general_DetectX : boost::mpl::and_<
    boost::is_class<T>,
    DetectX<T> >::type
{ };

bool hasX = general_DetectX<int>::value; // hasX = false

但是这个问题是为什么原DetectX<T>会产生错误而不是做SFINAE事情。以下是链接代码相关部分的摘录(本地结构FallbackCheck<U,U>以及typedef ArrayOfOneArrayOfTwotype为了简洁而删除了以下内容:

template<typename T>
class DetectX
{
      struct Derived : T, Fallback { };

      template<typename U> 
      static ArrayOfOne & func(Check<int Fallback::*, &U::X> *);

      template<typename U> 
      static ArrayOfTwo & func(...);

  public:
      enum { value = sizeof(func<Derived>(0)) == 2 };
};

可以看出DetectX::Derived在任何重载决策之外使用,因此永远不会调用处理错误的SFINAE规则。但是,这可以更改为使用Derived 作为重载解析的一部分发生的地方:

template<typename T>
class DetectX
{
    template<typename U>
    struct Derived : U, Fallback { };

    template<typename U>
    static ArrayOfOne & func(Check<int Fallback::*, &Derived<U>::X> *);

    template<typename U>
    static ArrayOfTwo & func(...);

public:
    enum { value = sizeof(func<T>(0)) == 2 };
};

Derived<T>模板仅在尝试实例化第一个func()重载时实例化,那么为什么即使修改后的版本仍然会出错?这是一个例子:

$ g++ --version | head -n1
g++ (GCC) 4.8.2
$ g++ -c demo.cxx
demo.cxx: In instantiation of 'struct DetectX<int>::Derived<int>':
demo.cxx:16:53:   required by substitution of 'template<class U> static char (& DetectX<T>::func(DetectX<T>::Check<int DetectX<T>::Fallback::*, (& DetectX<T>::Derived<U>::X)>*))[1] [with U = U; T = int] [with U = int]'
demo.cxx:24:31:   required from 'class DetectX<int>'
demo.cxx:27:25:   required from here
demo.cxx:7:12: error: base type 'int' fails to be a struct or class type
     struct Derived : U, Fallback { };
            ^

1 个答案:

答案 0 :(得分:3)

您可以使用:(https://ideone.com/LArNVO

#include <cstdint>
#include <type_traits>

#define DEFINE_HAS_MEMBER(traitsName, memberName)                             \
    template <typename U>                                                     \
    class traitsName                                                          \
    {                                                                         \
    private:                                                                  \
        struct Fallback { int memberName; };                                  \
        struct Dummy {};                                                      \
        template<typename T, bool is_a_class = std::is_class<T>::value>       \
        struct identity_for_class_or_dummy { using type = Dummy; };           \
        template<typename T>                                                  \
        struct identity_for_class_or_dummy<T, true> { using type = T; };      \
                                                                              \
        template <typename Base>                                              \
        struct Derived : Base, Fallback {};                                   \
        template<typename T, T> struct helper;                                \
        template<typename T>                                                  \
        static std::uint8_t                                                   \
        check(helper<int (Fallback::*),                                       \
              &Derived<typename identity_for_class_or_dummy<T>::type>::memberName>*); \
        template<typename T> static std::uint16_t check(...);                 \
    public:                                                                   \
        static                                                                \
        constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint16_t);  \
    }

DEFINE_HAS_MEMBER(has_foo, foo);

// Now test it:
class C{ public: int foo; };
class D : public C {};
class E {};

static_assert(has_foo<C>::value, "");
static_assert(has_foo<D>::value, "");
static_assert(!has_foo<E>::value, "");
static_assert(!has_foo<int>::value, "");

您可以在以下问题What is exactly the “immediate context” mentioned in the C++11 Standard for which SFINAE applies?

中理解为什么它会失败

简而言之,SFINAE适用于Derived<U>::X但不适用Derived<U>(在您的情况下形成不良)。