2015-09-14 31 views
28

我想写一个类型特质来检查某些类型是否有成员member。如果member公共,有任何数量的方式来做到这一点(如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 

但是,如果该成员是私人,这个特质失败了,因为member上的访问是不合格的(我们不是朋友),并且由于访问原因而形成的不合格和由于这个事物而形成的不合格之间没有区别 - 存在原因:

class D { 
    int member; 
}; 

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

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

+13

不是想说这是个坏主意。我没有资格这样做。但我真的很好奇,会有什么用呢?也就是说,查询某种类型的“私人”特性的用例是什么(你无法以任何方式访问)? – DevSolar

+0

请参阅http://stackoverflow.com/questions/257288/possible-for-c-template-to-check-for-a-functions-existence#264088 – mvw

+7

如果'member'是私人的,您应该可以安全地重命名该成员不会破坏外部代码,而只是恰好在使用您的类。能够按照您使用的方式创建'has_member'模板将使其非常容易创建代码,至少不会以另一种开发人员合理期望的代码行事的方式行事。所以我分享DevSolar的问题:你有什么用途? – hvd

回答

22

确实存在对非最终非工会类类型的方式:

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-表达) 的一类范围(3.3.7)的含义。名称查询可能会导致含糊不清, 这种情况下该程序是格式不正确的。 [...]名称查找发生在访问控制(3.4,第11章)之前 。

注意,因为它忽略了类型名称说明符 S表示查找无类型名称GCC的查找,只要打破。

+0

我总是发现它完全违反直觉,查找独立于保护。奇怪的是,C++允许标识符在某些情况下是* complication *,根本不*有用*。 – Sneftel

4

您可以创建另一个类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 

但是,这肯定闻起来像一个肮脏的黑客我。