2012-07-19 12 views
6

Ho il seguente problema:C++ come differenziare tra modello per contenitore e tipo nativo

template<class T> 
void set(std::string path, const T data) 
{ 
    stringstream ss; 
    ss << data << std::endl; 
    write(path, ss.str(); 
} 

template<class T> 
void set(std::string path, const T data) 
{ 
    std::stringstream ss; 
    for(typename T::const_iterator it = data.begin(); it < data.end(); ++it) 
    { 
     ss << *it; 
     if(it < data.end() -1) 
      ss << ", "; 
    } 
    ss << std::endl; 
    write(path, ss.str()); 
} 

ottengo il seguente errore:

error: ‘template<class T> void myclass::set(std::string, T)’ cannot be overloaded 
error: with ‘template<class T> void myclass::set(std::string, T)’ 

C'è un modo per distinguere tra tipi di contenitori e altri tipi nei modelli?

+0

caratteri morfologici. Sembra che tu abbia bisogno di creare il tuo tratto is_container (http://stackoverflow.com/questions/7617203/is-it-possible-to-use-type-traits-to-check-whether-a-type-is-a -contenitore). (Non l'ho fatto prima, e sto solo aspettando una compilazione per completare, quindi non una risposta completa - scusa.Ma sarei interessato alla soluzione;)) – peterchen

+0

Vuoi 'const T & data', e' ss << data' ?? –

+0

Questa è * quasi * una grande domanda, ma ci sono alcuni errori di sintassi che hanno reso la risposta più difficile del necessario. ('T' dovrebbe essere' data' sulla terza riga della prima forma di 'set' e le tue chiamate a' write' mancano di un ')' ma sono ancora errate dopo averlo aggiunto. – Flexo

risposta

6

in C++ 03 si può fare questo con un po 'di SFINAE per abilitare selettivamente diverse versioni della funzione per diversi tipi:

#include <boost/type_traits.hpp> 
#include <sstream> 
#include <iostream> 
#include <vector> 

using namespace std; 

template<class T> 
void set(typename boost::enable_if<boost::is_pod<T>, std::string>::type path, const T data) 
{ 
    std::cout << "POD" << std::endl; 
    stringstream ss; 
    ss << data << std::endl; 
} 

template<class T> 
void set(typename boost::disable_if<boost::is_pod<T>, std::string>::type path, const T data) 
{ 
    std::cout << "Non-POD" << std::endl; 
    std::stringstream ss; 
    for(typename T::const_iterator it = data.begin(); it < data.end(); ++it) 
    { 
     ss << *it; 
     if(it < data.end() -1) 
      ss << ", "; 
    } 
    ss << std::endl; 
} 

int main() { 
    int i; 
    float f; 
    std::vector<int> v; 
    set("", v); 
    set("", i); 
    set("", f); 
} 

ho usato spinta per comodità qui, ma si potrebbe rotolare il vostro proprio se boost non è un'opzione o usare invece C++ 11.

is_pod non è proprio quello che si vuole veramente, probabilmente avrete bisogno di un is_container tratto, ma that's not so trivial, you'll need to make a trait of your own e is_pod fa una buona approssimazione per come utilizzare i tratti per abilitare selettivamente funzioni come risposta semplice.

8

Uso un tratto:

#include <type_traits> 

template <typename T> 
typename std::enable_if<is_container<T>::value>::type 
set (std::string const & path, T const & container) 
{ 
    // for (auto const & x : container) // ... 
} 


template <typename T> 
typename std::enable_if<!is_container<T>::value>::type 
set (std::string const & path, T const & data) 
{ 
    std::ostringstream oss; 
    oss << data; 
    write(path, oss.str()); 
} 

è possibile trovare un tratto adeguato nel pretty printer code.

0

Come hanno scritto i miei predecessori, devi usare un qualche tipo di tratto. Probabilmente si dovrebbe usare Boost per questo, ma se non si vuole si può semplicemente usare qualcosa di simile (http://ideone.com/7mAiB):

template <typename T> 
struct has_const_iterator { 
    typedef char yes[1]; 
    typedef char no[2]; 

    template <typename C> static yes& test(typename C::const_iterator*); 
    template <typename> static no& test(...); 

    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 

template <bool> class bool2class {}; 

template <class T> 
void set_inner(const std::string &path, T & var, bool2class<false> *) { 
     // T is probably not STL container 
} 

template <class T> 
void set_inner(const std::string &path, T & var, bool2class<true> *) { 
     // T is STL container 
} 

template <class T> 
void set(const std::string &path, T &var) { 
     set_inner(path, var, (bool2class<has_const_iterator<T>::value>*)0); 
} 

Non è un compito facile distinguere contenitore dal semplice array, così ho utilizzato qui controllando se il tipo ha const_iterator. Probabilmente dovresti anche controllare se ha begin(), end() e altre cose che useresti nel codice futuro.

1

Si potrebbe provare qui la tecnica di sostituzione guasti non è un errore (SFINAE).

In primo luogo, è necessario una funzione per determinare se un tipo ha un membro iteratore ...

template <typename T> 
struct Has_Iterator 
{ 
    template <typename> 
    static char test(...); 

    template <typename U> 
    static int test(typename U::const_iterator*); 

    static const bool result = sizeof test<T>(0) != sizeof(char); 
}; 

Nel codice precedente, Visual C++ richiede test(typename U::const_iterator*) da utilizzare di preferenza alla vaga" .. . "parametro match, purché Usia in realtà una struct/class con un tipo di membro const_iterator. In caso contrario, si ha un "errore di sostituzione" - che non è un errore irreversibile di compilazione (quindi SFINAE), e il tentativo di trovare una funzione di corrispondenza è saturato da test(...). Poiché i tipi di ritorno delle due funzioni sono diversi, l'operatore sizeof può verificare quale è stato abbinato, impostando il valore booleano result in modo appropriato.

Quindi è possibile inoltrare le richieste di stampare cose da specializzazioni modello che li supportano ...

template <typename T> 
void print(const T& data) 
{ 
    printer<Has_Iterator<T>::result, T>()(data); 
} 

// general case handles types having iterators... 
template <bool Has_It, typename T> 
struct printer 
{ 
    void operator()(const T& data) 
    { 
     for (typename T::const_iterator i = data.begin(); i != data.end(); ++i) 
      std::cout << *i << ' '; 
     std::cout << '\n'; 
    } 
}; 

// specialisation for types lacking iterators... 
template <typename T> 
struct printer<false, T> 
{ 
    void operator()(const T& data) 
    { 
     std::cout << data << '\n'; 
    } 
}; 
+0

Nitpick: *" l'operatore 'sizeof' può testare quale è stato chiamato" * - 'test()' è mai veramente chiamato. Lo sai già, ne sono sicuro. – cdhowie

+0

@cdhowie: fair point - risposta aggiornata con "abbinato". Saluti. –

Problemi correlati