SFINAE用于类型实例化



我遇到了c++中的成员检测器(Member Detector)习语,它是一种类型trait,用于判断类是否包含具有特定名称的成员。但是,如果类型不是类,则链接的示例不会像我预期的那样工作:我想要任何非类类型的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>以及类型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 { };
            ^

您可以使用:(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, "");

你可以理解为什么它失败在你的情况下,以下问题是什么是确切的"直接上下文"在c++ 11标准中提到的SFINAE适用?

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

最新更新