2011-10-20 15 views
11

Obecnie wykonuję niektóre metaprogramming szablonu. W moim przypadku mogę obsłużyć dowolny typ "iterowalny", tj. Dowolny typ, dla którego typedef foo const_iterator istnieje w ten sam sposób. Próbowałem użyć nowego metaprogramowania szablonu C++ 11, ale nie mogłem znaleźć metody wykrywania, czy dany typ nie istnieje.wykrywanie typedef w czasie kompilacji (metaprogramming szablonu)

Ponieważ muszę również włączać i wyłączać inne specjalizacje szablonów w oparciu o inne cechy, używam obecnie szablonu z dwoma parametrami, a drugi jest produkowany za pośrednictwem std::enable_if. Oto co mam aktualnie robi:

template <typename T, typename Enable = void> 
struct Foo{}; // default case is invalid 

template <typename T> 
struct Foo< T, typename std::enable_if<std::is_fundamental<T>::value>::type>{ 
    void do_stuff(){ ... } 
}; 

template<typename T> 
struct exists{ 
    static const bool value = true; 
}; 

template<typename T> 
struct Foo<T, typename std::enable_if<exists< typename T::const_iterator >::value >::type> { 
    void do_stuff(){ ... } 
}; 

nie byłem w stanie zrobić coś takiego bez szablonu exists pomocnika. Na przykład po prostu robi

template<typename T> 
struct Foo<T, typename T::const_iterator> { 
    void do_stuff(){ ... } 
}; 

nie działa, ponieważ w tych przypadkach, w których powinny być stosowane to specjalizacja, nieważne domyślne sprawa została instancja zamiast.

Jednak nie mogłem znaleźć tego exists w dowolnym miejscu w nowym standardzie C++ 11, który, o ile wiem, po prostu bierze od boost::type_traits dla tego rodzaju rzeczy. Jednak w przypadku modelu homepage dla boost::type_traits nie jest wyświetlane żadne odniesienie do niczego, co można zamiast tego wykorzystać.

Czy brakuje tej funkcji, czy też przeoczyłem inny oczywisty sposób osiągnięcia pożądanego zachowania?

Odpowiedz

13

Jeśli po prostu chcesz, jeśli dany typ zawiera const_iterator, poniżej jest uproszczona wersja n kodzie:

template<typename T> 
struct void_ { typedef void type; }; 

template<typename T, typename = void> 
struct Foo {}; 

template<typename T> 
struct Foo <T, typename void_<typename T::const_iterator>::type> { 
     void do_stuff(){ ... } 
}; 

Zobacz this answer jakiegoś wyjaśnienia, w jaki sposób działa ta technika.

+3

Powinieneś zamieścić link do swoich pytań na temat tego, jak ten działa. :) Wygląda na to, że polubiłeś to, widząc, że już kilka razy to sugerujesz. – Xeo

+0

@Xeo, tak to całkiem proste i proste. Ale nie dostaję twojej pierwszej części "Powinieneś zamieścić link do twoich pytań o to, jak ten działa. :) ... masz na myśli, że podczas odpowiadania mam umieścić link do moich poprzednich pytań (zamiast kodu samo) ? Podejrzewam, że nie jest to zalecane na SO. – iammilind

+1

Nono, chodzi mi o to, że zamieścisz link do [twoje pytanie, w którym zapytałeś, jak to działa] (http://stackoverflow.com/questions/6543652/different-template-syntax-for-finding-if-argument -is-a-class-or-not), ponieważ na początku nie jest to tak oczywiste. Woops, i właśnie zauważyłem, że napisałem "pytania", oznaczało tylko jedno pytanie oczywiście. – Xeo

7

Można utworzyć cechę has_const_iterator, która zapewnia wartość boolowską i używa jej w specjalizacji.

Coś takiego może zrobić:

template <typename T> 
struct has_const_iterator { 
private: 
    template <typename T1> 
    static typename T1::const_iterator test(int); 
    template <typename> 
    static void test(...); 
public: 
    enum { value = !std::is_void<decltype(test<T>(0))>::value }; 
}; 

a następnie można specjalizować się tak:

template <typename T, 
      bool IsFundamental = std::is_fundamental<T>::value, 
      bool HasConstIterator = has_const_iterator<T>::value> 
struct Foo; // default case is invalid, so no definition! 

template <typename T> 
struct Foo< T, true, false>{ 
    void do_stuff(){// bla } 
}; 

template<typename T> 
struct Foo<T, false, true> { 
    void do_stuff(){//bla} 
}; 
+1

Czy istnieje powód, dla którego wybraliśmy ' test (int) 'zamiast np.' test() '' Tylko upewniając się, że rozumiem, jak działa tutaj rezerwa przeciążania. Dzięki! – nknight

+2

@ nocnight: Powód: aby wywołanie 'testu' było jednoznaczne. – erenon

4

Oto kolejna wersja czeku typ członkiem cecha:

template<typename T> 
struct has_const_iterator 
{ 
private: 
    typedef char      yes; 
    typedef struct { char array[2]; } no; 

    template<typename C> static yes test(typename C::const_iterator*); 
    template<typename C> static no test(...); 
public: 
    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 
1

Istnieje kilka sposobów, aby to zrobić. W C++ 03, można użyć impuls i enable_if zdefiniować cechę (docs, source):

BOOST_MPL_HAS_XXX_TRAIT_DEF(const_iterator); 

template <typename T, typename Enable = void> 
struct Foo; 

template <typename T> 
struct Foo< T, typename boost::enable_if<boost::is_fundamental<T> >::type>{ 
    void do_stuff(){ ... } 
}; 

template<typename T> 
struct Foo<T, typename boost::enable_if<has_const_iterator<T> >::type> { 
    void do_stuff(){ ... } 
}; 

w C++ 11, można użyć Tick tak:

TICK_TRAIT(has_const_iterator) 
{ 
    template<class T> 
    auto require(const T&) -> valid< 
     has_type<typename T::const_iterator> 
    >; 
}; 

template <typename T, typename Enable = void> 
struct Foo; 

template <typename T> 
struct Foo< T, TICK_CLASS_REQUIRES(std::is_fundamental<T>::value)>{ 
    void do_stuff(){ ... } 
}; 

template<typename T> 
struct Foo<T, TICK_CLASS_REQUIRES(has_const_iterator<T>())> { 
    void do_stuff(){ ... } 
}; 

Również z Tick możesz dodatkowo ulepszyć cechę, aby faktycznie wykryć, że const_iterator jest w rzeczywistości także iteratorem.Więc powiedzieć, że określenie prostego is_iterator cechę takiego:

TICK_TRAIT(is_iterator, 
    std::is_copy_constructible<_>) 
{ 
    template<class I> 
    auto require(I&& i) -> valid< 
     decltype(*i), 
     decltype(++i) 
    >; 
}; 

Możemy zdefiniować has_const_iterator cechę, by sprawdzić, czy typ const_iterator dopasowuje is_iterator cechę takiego:

TICK_TRAIT(has_const_iterator) 
{ 
    template<class T> 
    auto require(const T&) -> valid< 
     has_type<typename T::const_iterator, is_iterator<_>> 
    >; 
}; 
Powiązane problemy