如何检查 class 中是否存在成员名称(变量或函数),是否指定类型?

How to check if a member name (variable or function) exists in a class, with or without specifying type?

这个问题是以下内容的扩展:
Templated check for the existence of a class member function?

是否有任何实用程序可以帮助查找:

使用 std::experimental::is_detected and std::experimental::disjunction 你可以这样做:

//check for a type member named foo
template <typename T>
using foo_type_t = typename T::foo;

//check for a non-type member named foo
template <typename T>
using foo_non_type_t = decltype(&T::foo);

template <typename T>
using has_foo = disjunction<is_detected<foo_type_t, T>,
                            is_detected<foo_non_type_t, T>>;

然后你就可以随意使用has_foo<my_class>::value

以上内容不仅仅适用于类型和成员函数,但您可以根据需要使用 std::is_member_function_pointerstd::is_member_object_pointer 等特征轻松约束它。

要提供您的可选参数,您可以使用 std::experimental::is_detected_exact 助手。

Live Demo


请注意,如果您从我链接的页面中获取上述特征的实现,则可以将其与 C++14 一起使用。对 disjunction 代码稍作改动即可在 C++11 中使用它。

对于非 C++17:

Boost Type Traits 库 has metafunctions 用于检查是否存在具有给定名称的成员或通过提供签名进行更细粒度的控制。

以下是使用稍微修改过的 HAS_MEMBER 宏的程序:

#include <iostream>
#include <type_traits>

#define DECLARE_HAS_MEMBER(__trait_name__, __member_name__)                          \
                                                                                     \
    template <typename __boost_has_member_T__>                                       \
    class __trait_name__                                                             \
    {                                                                                \
        using check_type = ::std::remove_const_t<__boost_has_member_T__>;            \
        struct no_type {char x[2];};                                                 \
        using  yes_type = char;                                                      \
                                                                                     \
        struct  base { void __member_name__() {}};                                   \
        struct mixin : public base, public check_type {};                            \
                                                                                     \
        template <void (base::*)()> struct aux {};                                   \
                                                                                     \
        template <typename U> static no_type  test(aux<&U::__member_name__>*);       \
        template <typename U> static yes_type test(...);                             \
                                                                                     \
        public:                                                                      \
                                                                                     \
        static constexpr bool value = (sizeof(yes_type) == sizeof(test<mixin>(0)));  \
    }

struct foo
{
    int bar(){}
};

struct baz
{};

DECLARE_HAS_MEMBER(has_bar, bar);

int main()
{
    std::cout << has_bar<foo>::value << '\n' << has_bar<baz>::value;
}

如链接页面中所述,它使用了这样一个事实,即如果您从两个具有相同名称的成员的 类 继承,尝试使用此名称将变得不明确。

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);  // Creates a SFINAE `class HasMember_Foo`
HasMember(i);    // Creates a SFINAE `class HasMember_i`

现在我们可以利用 HasMember_X 在任何 class 中检查 X,如下所示:

#include<iostream>
struct S
{
  void Foo () const {}
//  void Foo () {}  // If uncommented then type should be mentioned in `HasMember_Foo`    
  int i;
};
int main ()
{
  std::cout << HasMember_Foo<S, void (S::*) () const>::value << "\n";
  std::cout << HasMember_Foo<S>::value << "\n";
  std::cout << HasMember_i<S, int (S::*)>::value << "\n";
  std::cout << HasMember_i<S>::value << "\n";
}

捕获量

  1. 对于方法,如果我们不提及类型,那么 class 不能有重载的方法。如果有,那么这个技巧就失败了。 即,即使指定成员出现不止一次,结果仍将是 false.
  2. 如果成员是基础class的一部分,那么这个技巧就失败了;例如如果 BS 的基础并且存在 void B::Bar (),则 HasMember_Bar<S, void (B::*)()>::valueHasMember_Bar<S, void (S::*)()>::valueHasMember_Bar<S>::value 将给出 false