如何检测类中是否确实存在特定的成员变量?

时间:2016-04-13 16:01:41

标签: c++ templates variables member

我故意使用与this question完全相同的标题,因为我觉得接受的答案并没有解释我遇到的问题。

我正在寻找一种方法来检测某个类是否有某个成员变量。值得注意的是,我正在寻找变量,而不是成员函数或其他任何东西。

以下是我链接的问题中提供的示例:

template<typename T> struct HasX { 
    struct Fallback { int x; }; // introduce member name "x"
    struct Derived : T, Fallback { };

    template<typename C, C> struct ChT; 

    template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1]; 
    template<typename C> static char (&f(...))[2]; 

    static bool const value = sizeof(f<Derived>(0)) == 2;
}; 

struct A { int x; };
struct B { int X; };

int main() { 
    std::cout << HasX<A>::value << std::endl; // 1
    std::cout << HasX<B>::value << std::endl; // 0
}

但是如果我们做像

这样的事情,我们会得到相同的输出
template<typename T> struct HasX { 
    struct Fallback { int x; }; // introduce member name "x"
    struct Derived : T, Fallback { };

    template<typename C, C> struct ChT; 

    template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1]; 
    template<typename C> static char (&f(...))[2]; 

    static bool const value = sizeof(f<Derived>(0)) == 2;
}; 

struct A { 
  void x()
  {
  }
};
struct B { int X; };

int main() { 
    std::cout << HasX<A>::value << std::endl; // 1
    std::cout << HasX<B>::value << std::endl; // 0
}

(请注意,在第二个示例中,int x中的A已被成员函数void x()替换。)

我对如何解决这个问题一无所知。我通过执行类似

的操作来部分修复此问题
template <bool, typename> class my_helper_class;

template <typename ctype> class my_helper_class <true, ctype>
{
  static bool const value = std :: is_member_object_pointer <decltype(&ctype :: x)> :: value;
};

template <typename ctype> class my_helper_class <false, ctype>
{
  static bool const value = false;
};

template <typename T> struct HasX
{
// ...

static bool const value = my_helper_class <sizeof(f <Derived>(0)) == 2, T> :: value;
};

如果我正在使用对象,它实际上会选择。但是,如果我的班级中有更多具有相同名称x的重载函数,则上述操作无效。

例如,如果我

struct A
{
   void x()
   {
   }

   void x(int)
   {
   }
};

然后指针未成功解析,对HasX <A>的调用无法编译。

我该怎么办?是否有任何解决方法或更简单的方法来完成这项工作?

1 个答案:

答案 0 :(得分:2)

问题是HasX仅检查名称 x是否存在。如果... 不明确,则&C::x会被选中(如果它与FallbackT都匹配,则会发生这种情况)。仅当ChT<>正好&C::x时才会选择Fallback::x重载。我们实际上没有检查T::x的类型 - 所以我们从来没有真正检查x是变量还是函数或其他什么。

解决方案是:使用C ++ 11并检查&T::x是否为成员对象指针:

template <class T, class = void>
struct HasX
: std::false_type
{ };

template <class T>
struct HasX<T,
    std::enable_if_t<
        std::is_member_object_pointer<decltype(&T::x)>::value>
    >
: std::true_type { };

如果&T::x不存在,则替换失败,我们会回退到主模板并获取false_type。如果&T::x存在,但名称过载,则替换失败。如果&T::x存在但是是非重载函数,则enable_if_t<false>上的替换失败。 SFINAE获胜。

适用于所有这些类型:

struct A { 
  void x()
  {
  }

  void x(int)
  {
  }
};

struct B { int X; };
struct C { int x; };
struct D { char x; };

int main() { 
    static_assert(!HasX<A>::value, "!");
    static_assert(!HasX<B>::value, "!");
    static_assert(HasX<C>::value, "!");
    static_assert(HasX<D>::value, "!");
}