【问题标题】:Detect existence of private member检测私有成员的存在
【发布时间】:2015-12-10 13:26:01
【问题描述】:

我想写一个类型特征来检查某个类型是否有成员member。如果memberpublic,那么有很多方法可以做到这一点(例如void_t),其中最简洁的可能是Yakk's can_apply(最终可以称为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

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

【问题讨论】:

  • 不要说这是一个坏主意。我没有资格这样做。但是我真的很好奇,这有什么用?即,查询 private 类型的特征(无论如何您都无法访问)的用例是什么?
  • 如果member 是私有的,您应该能够安全地重命名该成员,而不会破坏恰好使用您的类的外部代码。能够以您使用的方式创建has_member 模板将使创建代码变得非常容易,至少不会像其他开发人员可能合理期望的代码那样运行。所以我分享 DevSolar 的问题:你有什么用?
  • @DevSolar 我真的很想知道这样的事情是否可行。名称查找本身不考虑访问权限,因此似乎应该可以包装类型特征。

标签: c++ templates c++11 c++14 typetraits


【解决方案1】:

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

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。诀窍是检查查找不同的基类是否会产生歧义。 [class.member.lookup]/2:

成员名称查找确定名称的含义(id-expression) 在类范围(3.3.7)中。名称查找可能会导致歧义,在 在这种情况下,程序格式错误。 [...] 名称查找发生 在访问控制之前(3.4,第 11 条)。

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

【讨论】:

  • 我总是发现查找独立于保护是完全违反直觉的。奇怪的是,C++ 允许标识符在某些情况下是一个复杂而根本没有有用
【解决方案2】:

您可以创建另一个确实具有该成员的类MemberBase,然后将这两个类(检查TBaseMember的类)子类化并尝试访问该成员子类。如果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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-23
    • 1970-01-01
    • 1970-01-01
    • 2021-09-15
    • 1970-01-01
    相关资源
    最近更新 更多