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

Type trait: Check if class have specific function (maybe inherit)

我知道有很多方法可以检测 class 是否具有特定功能,但没有一种方法真正适用于我的具体情况。我当前用于检查正确成员函数的实现工作正常,继承函数除外。

#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

但是我必须知道基数 class 并且应该避免这种情况。

有谁知道如何同时检查派生函数吗?

编辑: 如果根本不存在 Foo,类型特征也应该起作用。

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

这是一种方法(适用于您的 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)){};
};

这是一种老式的 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,1 个有类型,1 个没有类型。它们适用于任何类型(不只是特定于 int (X::*)(float))。如果没有提到类型,那么 class 必须只有 1 个这样的方法(没有重载)。因此,提及类型总是更安全;正如您在问题中所做的那样,具体类型是 int (X::*)(float)。顺便说一句,这也可以使用另一个宏来包含。
如果没有这样的额外宏,在 class Cclass D 的情况下,您可能必须指定方法的类型。

这是包含您的代码的 demo


这里假定无论选择哪个 class 成员(函数或变量),都必须是 public 作用域。即如果 X::fooprivate 那么这个解决方案将不起作用。

这是 <experimental/type_traits> or <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;
}