2015-09-14 29 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 

但是,這肯定聞起來像一個骯髒的黑客我。