类型特征:检查 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.");
此实现不适用于 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::foo
是 A::*
(而不是 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 C
和 class D
的情况下,您可能必须指定方法的类型。
这是包含您的代码的 demo。
这里假定无论选择哪个 class 成员(函数或变量),都必须是 public
作用域。即如果 X::foo
是 private
那么这个解决方案将不起作用。
这是 <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;
}
我知道有很多方法可以检测 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.");
此实现不适用于 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::foo
是 A::*
(而不是 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 C
和 class D
的情况下,您可能必须指定方法的类型。
这是包含您的代码的 demo。
这里假定无论选择哪个 class 成员(函数或变量),都必须是 public
作用域。即如果 X::foo
是 private
那么这个解决方案将不起作用。
这是 <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;
}