类型特征:检查类是否具有特定功能(可能是继承)

时间:2016-04-18 11:12:54

标签: c++ c++11 typetraits

我知道有很多可能的方法可以检测一个类是否具有特定的功能但是它们的确不适用于我的确切情况。除了继承函数之外,我当前检查正确成员函数的实现是否有效。

#include <type_traits>

template<typename T>                                                                
class HasFoo {                                                                                 
    template <typename U, int (U::*)(float)>                                  
      struct Check;                                                                 

    template <typename U>                                                       
      static std::true_type Test(Check<U, &U::foo> *);                 

    template <typename U>                                                           
      static std::false_type Test(...);                                               

public:
    static constexpr bool value = decltype(Test<T>(0))::value;                    
};

struct A {
  int foo(float);
};

struct B : public A {
};

struct C {
  unsigned int foo(double);
};

struct D {
  static int foo(float);
};

static_assert(HasFoo<A>::value, "A should have foo.");
static_assert(HasFoo<B>::value, "B should inherit foo from A.");

static_assert(!HasFoo<C>::value, "C should not have foo.");
static_assert(!HasFoo<D>::value, "Ds static foo should be false.");

Live example.

此实现不适用于B的static_assert。

不可接受的解决方法是检查:

template <typename U, int (U::A::*)(float)>
struct Check;                 |
                              |- add base class

但是我必须知道基类,这应该避免。

有没有人知道如何检查衍生函数?

修改 如果根本不存在Foo,那么类型特征也应该有效。

struct E {};
static_assert(!HasFoo<E>::value, "E does not have foo.");

3 个答案:

答案 0 :(得分:3)

这是一种方法(为你的4个测试用例工作,虽然没有密集测试),感谢@ Jarod42的改进(见最后的初步答案):

template <typename T>
int call_foo (int (T::*)(float));

template <typename C>
std::true_type has_foo(decltype(call_foo(&C::foo)));

template <typename C>
std::false_type has_foo (...);    

template<typename T>
using HasFoo = decltype(has_foo<T>(0));

您的代码存在的问题是您期望U::*,而&B::fooA::*(不是B::*)。在这里,我让编译器通过使用隐式类型推导来选择T的值,因此我不会遇到这样的问题。

代码的工作原理如下:

  • 如果T没有foo成员,则编译器将选择has_foo的第二次重载。
  • 如果T确实有foo成员,编译器将尝试第一次重载但由于没有匹配的call_foo函数会失败,因此它将再次选择第二个并生成std::false_type

ideone上的工作代码:http://ideone.com/erh93I

如果您愿意,可以将所有内容放在class中:

template <typename T>
class HasFoo {

    template <typename C>
    static int call_foo (int (C::*)(float));

    template <typename C>
    static std::true_type has_foo (decltype(call_foo(&C::foo)));

    template <typename C>
    static std::false_type has_foo (...);

public:
    static constexpr bool value = decltype(has_foo<T>(0)){};
};

答案 1 :(得分:3)

这是一个古老的学校 C ++ 03 这样做的方式。通常,它可以用作实用程序,并为任何方法或变量进行模塑。

#define HasMember(NAME) \
  template<class Class, typename Type = void> \
  struct HasMember_##NAME \
  { \
    typedef char (&yes)[2]; \
    template<unsigned long> struct exists; \
    template<typename V> static yes Check (exists<sizeof(static_cast<Type>(&V::NAME))>*); \
    template<typename> static char Check (...); \
    static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \
  }; \
  template<class Class> \
  struct HasMember_##NAME<Class, void> \
  { \
    typedef char (&yes)[2]; \
    template<unsigned long> struct exists; \
    template<typename V> static yes Check (exists<sizeof(&V::NAME)>*); \
    template<typename> static char Check (...); \
    static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \
  }

实例化:

HasMember(Foo);

<强>用法

HasMember_Foo<B>::value  // without type (but then no overload allowed)
HasMember_Foo<C, int (C::*)(float)>::value  // needs type

请注意,这里我提供两个HasMember_Foo s,1个带类型,1个没有类型。它们针对任何类型进行推广(不仅仅针对int (X::*)(float))。如果没有提到的类型,那么该类必须只有一个这样的方法(没有重载)。因此,提及类型总是更安全;正如您在问题中所做的那样,具体类型为int (X::*)(float)。顺便说一句,这也可以使用另一个宏来包含 如果没有这样的额外宏,如果是class Cclass D,您可能需要指定方法的类型。

以下是您的代码demo

这里假设选择了哪个类成员(函数或变量),必须public作用域。即如果X::fooprivate,则此解决方案无效。

答案 2 :(得分:1)

这是带有 <experimental/type_traits><boost/type_traits.hpp> 的解决方案

#include <experimental/type_traits>
#include <iostream>

struct A {
  auto foo() { return 0; }
};
struct B {
  auto bar() { return 0.0; }
};
struct C : public A {
  auto bAr() { return 0.0; }
};
struct D : public C {
  auto baR() { return 0.0; }
};

template <typename T>
using HasFoo_t = decltype(std::declval<T&>().foo());

int main() {
  std::cout << std::experimental::is_detected_v<HasFoo_t, A> << std::endl;
  std::cout << std::experimental::is_detected_v<HasFoo_t, B> << std::endl;
  std::cout << std::experimental::is_detected_v<HasFoo_t, C> << std::endl;
  std::cout << std::experimental::is_detected_v<HasFoo_t, D> << std::endl;
}