检测私有成员的存在

Detect existence of private member

我想写一个类型特征来检查某个类型是否有成员 member。如果 memberpublic,有许多方法可以做到这一点(例如 void_t), the most concise of which is probably (which could eventually be called std::is_detected):

struct C {
    int member;
};

template <typename T>
using member_type = decltype(&T::member);

template <typename T>
using has_member = can_apply<member_type, T>;

static_assert(has_member<C>{}, "!"); // OK

但如果成员是 private,则此特征失败,因为 member 上的访问格式不正确(我们不是朋友)并且没有区别由于访问原因导致格式错误和由于此事物不存在原因导致格式错误之间:

class D {
    int member;
};

static_assert(has_member<D>{}, "!"); // error

有没有办法编写这样一个跨所有访问控制的成员检查器?

您可以创建另一个 class MemberBase 拥有该成员,然后子class 两个 class es(class检查TBaseMember)并尝试访问subclass的成员。如果 T 也有一个 member 成员,那么您将遇到歧义问题。

代码:

#include <type_traits>

// Yakk's can_apply

template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;

template<class...>struct types{using type=types;};
namespace details {
  template<template<class...>class Z, class types, class=void>
  struct can_apply : std::false_type {};
  template<template<class...>class Z, class...Ts>
  struct can_apply< Z, types<Ts...>, void_t< Z<Ts...> > >:
    std::true_type
  {};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,types<Ts...>>;

// Main code

class MemberBase {
    public:
        int member;
};

template<class ToCheck>
class MemberCheck: public ToCheck, public MemberBase {
};

template <typename T>
using member_type = decltype(&T::member);

template <typename T>
using hasnot_member = can_apply<member_type, MemberCheck<T>>;

template <typename T> 
using static_not = std::integral_constant<bool, !T::value>;

template <typename T>
using has_member = static_not<hasnot_member<T>>;

// Tests

class A {
    int member;
};

class Ap {
    public:
    int member;
};

class B {
    float member;
};

class C {
    int member();
};

class D {
};

static_assert(has_member<A>{}, "!"); // ok
static_assert(has_member<Ap>{}, "!"); // ok
static_assert(has_member<B>{}, "!"); // ok
static_assert(has_member<C>{}, "!"); // ok
static_assert(has_member<D>{}, "!"); // fail

但是,这对我来说绝对是一个肮脏的黑客。

对于非final非联合class类型确实有办法:

namespace detail {
    struct P {typedef int member;};
    template <typename U>
    struct test_for_member : U, P
    {
        template <typename T=test_for_member, typename = typename T::member>
        static std::false_type test(int);
        static std::true_type test(float);
    };
}
template <typename T>
using test_for_member =
  std::integral_constant<bool, decltype(detail::test_for_member<T>::test(0)){}>;

Demo。诀窍是检查查找不同的基数 classes 是否会产生歧义。 [class.member.lookup]/2:

Member name lookup determines the meaning of a name (id-expression) in a class scope (3.3.7). Name lookup can result in an ambiguity, in which case the program is ill-formed. […] Name lookup takes place before access control (3.4, Clause 11).

请注意,GCC 查找被破坏,因为它忽略了 typename-specifiers 中查找的非类型名称。