2016-03-25 13 views
0

Basierend auf an answer from Nawaz Ich möchte enable_if verwenden, um festzustellen, ob ein Vorlageargument ein Container ist oder nicht, und wenn es ist, möchte ich stattdessen eine benutzerdefinierte Nachricht für den Typnamen anzeigen des Namens von der Typid. Ich habe die Template-Spezialisierung auf zwei Arten implementiert. Der Code kompiliert und läuft, aber in keinem Fall wird die spezialisierte Methode aufgerufen. Ich nehme an, ich benutze enable_if falsch, was ist die richtige Anwendung hier?Std :: enable_if auf einem Vorlageargument zum Ermitteln des STL-Containers

Ich legte eine in sich geschlossene kleine Konsole Anwendung unter der Sequenz in dem Code ist: (a) erforderlichen Include-Dateien (b) die vorbereitende Vorlage Code (mit SFINAE) (c) zwei Implementierungen der Struktur, die angenommen werden die Aufgabe, (d) eine Client-Code

#include <typeinfo> 
#include <string> 
#include <list> 
#include <vector> 
#include <iostream> 
using namespace std; 

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); 
    typedef T type; 
}; 

template <typename T> 
struct has_begin_end 
{ 
    template<typename C> static char(&f(typename std::enable_if< 
     std::is_same<static_cast<typename C::const_iterator(C::*)() const>(&C::begin), 
     typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; 

    template<typename C> static char(&f(...))[2]; 

    template<typename C> static char(&g(typename std::enable_if< 
     std::is_same<static_cast<typename C::const_iterator(C::*)() const>(&C::end), 
     typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; 

    template<typename C> static char(&g(...))[2]; 

    static bool const beg_value = sizeof(f<T>(0)) == 1; 
    static bool const end_value = sizeof(g<T>(0)) == 1; 
}; 
template<typename T> 
struct is_container : std::integral_constant<bool, 
    has_const_iterator<T>::value && 
    has_begin_end<T>::beg_value && 
    has_begin_end<T>::end_value> 
{ }; 

struct TypeName { 
    template <typename T> 
    static const char* get() { 
     return typeid(T).name(); 
    } 

    template <typename T, typename std::enable_if<is_container<T>::value>::type > 
    static const char* get() 
    { 
     typedef typename T::value_type ElementType; 
     std:string containerType = ""; 
     if (std::is_same<decltype(std::vector<ElementType>), T>::value) { 
      containerType = "(Vector) "; 
     } 
     if (std::is_same<decltype(std::list<ElementType>), T>::value) { 
      containerType = "(List) "; 
     } 
     std::string returnString = "Container " + containerType; 
     returnString += " of "; 
     returnString += get<ElementType>(); 
     return returnString.c_str(); 
    } 
}; 

template <typename T> struct GypeName { 

    static const char* get() { 
     return typeid(T).name(); 
    } 

    template <class = typename std::enable_if<is_container<T>::value>::type > 
    static const char* get() 
    { 
     typedef typename T::value_type ElementType; 
     std:string containerType = ""; 
     if (std::is_same<decltype(std::vector<ElementType>), T>::value) { 
      containerType = "(Vector) "; 
     } 
     if (std::is_same<decltype(std::list<ElementType>), T>::value) { 
      containerType = "(List) "; 
     } 
     std::string returnString = "Container " + containerType; 
     returnString += " of "; 
     returnString += GypeName<ElementType>::get(); 
     return returnString.c_str(); 
    } 
}; 



int main(int argc, char** argv) { 
    cout << is_container<int>::value << endl; 
    cout << is_container<std::vector<int>>::value << endl; 
    cout << TypeName::get<int>() << endl; 
    cout << TypeName::get<std::string>() << endl; 
    cout << TypeName::get<std::vector<int>>() << endl; 
    cout << TypeName::get<std::vector<std::vector<int>>>() << endl; 
    cout << GypeName<int>::get() << endl; 
    cout << GypeName<std::string>::get() << endl; 
    cout << GypeName<std::vector<int>>::get() << endl; 
    cout << GypeName<std::vector<std::vector<int>>>::get() << endl; 
    return 0; 
} 

der Ausgang des ganzen ist

0 
1 
int 
class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > 
class std::vector<int,class std::allocator<int> > 
class std::vector<class std::vector<int,class std::allocator<int> >,class std::allocator<class std::vector<int,class std::allocator<int> > > > 
int 
class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > 
class std::vector<int,class std::allocator<int> > 
class std::vector<class std::vector<int,class std::allocator<int> >,class std::allocator<class std::vector<int,class std::allocator<int> > > > 

Nicht Aufruf der Spezialfunktion in jedem Fall auszuführen.

+0

Es gibt Tippfehler/Compiler-Fehler in bereitgestellten Code, [Demo ] (http://coliru.stacked-crooked.com/a/774f27cddb6363be) einmal behoben, (aber UB nicht behoben (Rückgabe const Char * von temporär)). – Jarod42

+0

Der Code erfordert Boost und verwendet C++ 11 (Visual Studio 2015) - Ich werde die Referenz entfernen und etwas in sich geschlossen geben .. –

+0

OK, Hauptproblem war enable_if sollte auf der Rückgabetyp nicht die Vorlage Argumente sein. Danke Jarod42. –

Antwort

1

können Sie Folgendes verwenden:

struct TypeName { 
    template <typename T> 
    static 
    std::enable_if_t<!is_container<T>::value, const char*> 
    get() { 
     return typeid(T).name(); 
    } 

    template <typename T> 
    static 
    std::enable_if_t<is_container<T>::value, std::string> 
    get() 
    { 
     typedef typename T::value_type ElementType; 
     std::string containerType = ""; 
     if (std::is_same<std::vector<ElementType>, T>::value) { 
      containerType = "(Vector) "; 
     } 
     if (std::is_same<std::list<ElementType>, T>::value) { 
      containerType = "(List) "; 
     } 
     return (boost::format("Container %s of %s") 
       % containerType 
       % TypeName::get<ElementType>()).str(); 
    } 
}; 

Demo

Beachten Sie, dass std::string als Behälter von char betrachtet wird.
Wie Sie spezifische (Laufzeit :() Fall für Vektor/Liste haben, können Sie nur Spezialisierung statt für diese beiden verwenden:

namespace detail 
{ 
    template <typename T> struct TypeName 
    { 
     auto operator()() const { return typeid(T).name(); } 
    }; 

    template <template <typename...>class C, typename T, typename...Ts> 
    struct TypeName<C<T, Ts...>> 
    { 
     auto operator()() const { 
      return (boost::format("container of %s") % TypeName<T>{}()).str(); 
     } 
    }; 

    template <typename T, typename A> 
    struct TypeName<std::vector<T, A>> 
    { 
     auto operator()() const { 
      return (boost::format("Vector of %s") % TypeName<T>{}()).str(); 
     } 
    }; 

    template <typename T, typename A> 
    struct TypeName<std::list<T, A>> 
    { 
     auto operator()() const { 
      return (boost::format("List of %s") % TypeName<T>{}()).str(); 
     } 
    }; 

} 

struct TypeName { 
    template <typename T> 
    static auto get() { 
     return detail::template TypeName<T>{}(); 
    } 
}; 

Demo

Verwandte Themen