2015-10-13 14 views
8

Łatwo jest napisać szablon, który wykrywa obecność konkretnego członka ramach typu używając void_t:wykrywania obecności dowolnego członka

#include <type_traits> 

// This comes from cppreference 
template<typename... Ts> struct make_void { typedef void type;}; 
template<typename... Ts> using void_t = typename make_void<Ts...>::type; 

// primary template handles types that have no ::aMember 
template< class T, class = void_t<> > 
struct has_aMember : std::false_type { }; 

// specialization recognizes types that do have a ::aMember 
template< class T > 
struct has_aMember<T, void_t<decltype(T::aMember)>> : std::true_type { }; 

Teraz, jeśli chcę, aby wykryć, czy jakiś inny członek jest obecny, musiałbym skopiować i wkleić szablony detektora i wystarczy zmienić aMember do otherMember:

template< class T, class = void_t<> > 
struct has_otherMember : std::false_type { }; 

template< class T > 
struct has_otherMember<T, void_t<decltype(T::otherMember)>> : std::true_type { }; 

chciałbym tego uniknąć kopiowania i wklejania i podać nazwę użytkownika jako parametr do bardziej generycznej wersji szablon detekcja:

template< class T, class member, class = void_t<> > 
struct has_arbitrary_member : std::false_type { }; 

template< class T, class member > 
struct has_arbitrary_member<T, void_t<decltype(T::member)>> : std::true_type { }; 

tak, że mogę korzystać z tego has_arbitrary_member przekazując typ i nazwę użytkownika jako parametr szablonu:

std::cout << has_arbitrary_member<MyType, aMember>(); 
std::cout << has_arbitrary_member<MyType, otherMember>(); 

Jednak z definicji ja przygotowanego powyżej, nie będzie to skompilować . Czy jest jakiś inny sposób na wdrożenie takiej funkcjonalności?

+0

Najlepsze, co możesz zrobić, to coś w rodzaju ['std :: experimental :: is_detected'] (http://en.cppreference.com/w/cpp/experimental/is_detected). –

+2

Co masz na myśli mówiąc "lubię"? Blok kodu * powyżej * jest * jak * te dwie linie. Jakie są wasze twarde wymagania i w jaki sposób blok kodu nad nimi nie spełnia waszych wymagań? Mógłbym napisać lepszą wersję twojego kodu powyżej (powiedzmy, z 'is_detected'), ale nie miałbym sposobu, jeśli tego właśnie chcesz. – Yakk

+0

@Yakk Dodałem zdanie na końcu pytania, aby sprecyzować moje zamiary. Oczywiście void_t nie jest wymogiem, chociaż widzę, że 'is_detected' jest zaimplementowane za pomocą samego' void_t'. czy to pomaga? – Rostislav

Odpowiedz

7

To, co próbujesz zrobić, nie jest ogólnie możliwe, ponieważ nie może tłumić wyszukiwania nazw na aMember, chyba że zmodyfikujesz strumień tokenów za pomocą makra. Wynika z tego, że nigdy nie możesz uciec z używaniem nazwy, która nie jest w zakresie, chyba że jesteś obecnie wewnątrz SFINAE, co zlikwiduje błąd.

Oznacza to, że naprawdę musisz napisać osobny szablon dla każdej nazwy.

Jest to jeden z tych przypadków, w których używanie makr nie jest złym pomysłem, ale zależy to od tego, czy naprawdę trzeba sprawdzić członków o konkretnej nazwie, czy też istnieją lepsze sposoby na osiągnięcie celu.

+0

To raczej pytanie wywołane ciekawością teoretyczną. Dziękuję za odpowiedź! – Rostislav

+0

@Rostislav Za to, co jest warte jego nie jest zbyt trudne zrobić ze stringification – soandos

3

Nie, nie można przekazać nazwy elementu jako parametru szablonu bez wskazania, z której klasy/struktury pochodzi nazwa elementu.

Jeśli ta klasa/struktura nie ma nazwy tego członka, oznacza to, że kod zawiera błąd.

Teraz void_t< decltype(&MyType::some_member) > ma wartość void lub błąd zastąpienia w zależności od tego, czy istnieje some_member. Często rozwiązuje to problem SFINAE, w którym nie ma potrzeby ustawiania czasu kompilacji.

Jeśli chcesz kod do uruchomienia, jeśli jakiś rodzaj nie posiada człon, można to zrobić z staranny dobór kolejności przeciążenia lub specjalizacji szablonu itp

To nie jest tak potężny jak has_arbitrary_member<MyType, some_member>.

template<class T> 
using some_member_type = decltype(std::declval<T>().some_member); 

jest prosta cecha, że ​​przy std::experimental::is_detected można zrobić:

is_detected_v< some_member_type, T > 

lub

template<class T> 
constexpr bool has_some_member_v = is_detected_v< some_member_type, T >; 

który zachowuje się jak has_arbitrary_member<MyType, some_member>. Uważam, że jest to najlepsze w praktyce.

+0

Dzięki! Miałem tylko nadzieję, że coś przeoczyłem, a w tym przypadku jest sposób na kopiowanie i wklejanie. Ale przynajmniej nauczyłem się czegoś nowego - rzeczy znalezionych. Tylko małe pytanie - czy to prawda, że ​​Library Fundamentals v2 to TS skierowane na C++ 17? – Rostislav