2015-09-14 11 views
28

Chcę napisać cechę typu, aby sprawdzić, czy jakiś typ ma członka member. Jeśli member były publicznego, istnieją różne sposoby, aby to zrobić (np void_t), z których najbardziej zwięzły jest prawdopodobnie Yakk's can_apply (które mogłyby ostatecznie nazwać std::is_detected):Wykryto istnienie członka prywatnego

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 

Ale jeśli członek byli prywatna, ta cecha zawodzi, ponieważ dostęp pod member jest źle sformułowany (nie jesteśmy przyjaciółmi) i nie ma rozróżnienia między źle uformowanymi z powodów dostępu i źle uformowanymi z powodu tego-rzeczy-nie- istnieją powody:

class D { 
    int member; 
}; 

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

Czy istnieje sposób na zapisanie takiego kontrolera dla wszystkich kontroli dostępu?

+13

Nie próbuje tego powiedzieć, to zły pomysł. Nie mam kwalifikacji, aby to zrobić. Ale jestem naprawdę ciekawa, jaki byłby pożytek z tego? Np. Jaki jest przypadek użycia zapytań o "prywatne" cechy typu (do których nie miałeś dostępu w żaden sposób)? – DevSolar

+0

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

+7

Jeśli 'member' jest prywatne, powinieneś móc bezpiecznie zmienić nazwę ten członek bez łamania kodu zewnętrznego, który po prostu używa twojej klasy. Możliwość tworzenia szablonu 'has_member' w taki sposób, w jaki go używasz, sprawi, że * bardzo * łatwo będzie stworzyć kod, który przynajmniej nie działa w taki sposób, w jaki inny programista mógłby rozsądnie oczekiwać działania kodu. Podzielam więc pytanie DevSolar: jaki masz na to pożytek? – hvd

Odpowiedz

22

Jest to rzeczywiście sposób na non-końcowych spoza Unii typy klas:

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. Sztuką jest sprawdzenie, czy wyszukiwanie w różnych klasach bazowych przyniesie niejednoznaczność. [Class.member.lookup]/2:

użytkownika nazwa odnośników określa znaczenie opisem (ID ekspresyjnego) w zakresie klasy (3.3.7). Wyszukiwanie nazw może prowadzić do niejednoznaczności , w przypadku której program jest źle sformułowany. [...] Przeszukiwanie nazw odbywa się pod kontrolą (3.4, klauzula 11).

Zauważ, że GCCs wyszukiwania jest uszkodzony w jakim pomija nazwy typu non-dla odnośnika w TypeName-specyfikatorem ów.

+0

Zawsze uważałem za całkowicie sprzeczne z intuicją, że wyszukiwanie było niezależne od ochrony. Dziwne, że C++ pozwala na identyfikatory, które w pewnych kontekstach są * komplikacją * bez bycia w ogóle * użytecznym *. – Sneftel

4

Można utworzyć inną klasę MemberBase że ma mieć ten element, a następnie podklasy dwóch klas (klasa sprawdzić T i BaseMember) i spróbuj uzyskać dostęp do członka podklasy. Jeśli T ma również element member, pojawi się problem z niejednoznacznością.

Kod:

#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 

to jednak zdecydowanie pachnie brudnej włamania do mnie.

Powiązane problemy