2012-07-19 9 views
6

Tengo el siguiente problema:C++ cómo diferenciar entre plantilla para envase y nativo de tipo

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()); 
} 

me sale el siguiente error:

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

¿Hay una manera de diferenciar entre tipos de contenedores y otros tipos en plantillas?

+0

rasgos de tipo. Parece que necesitas crear tu propio rasgo is_container (http://stackoverflow.com/questions/7617203/is-it-possible-to-use-type-traits-to-check-whether-a-type-is-a -envase). (No he hecho esto antes, y estoy esperando que se complete una construcción, por lo que no es una respuesta completa, lo siento.Pero me interesaría la solución;)) – peterchen

+0

¿Quieres 'const T & data', y' ss << data' ?? –

+0

Esta es * casi * una gran pregunta, pero hay algunos errores de sintaxis que hicieron que responder más difícil de lo necesario. ('T' debe ser' data' en la tercera línea de la primera forma de 'set' y tus llamadas a' write' faltan '' 'pero aún están equivocadas después de agregar eso). – Flexo

Respuesta

6

en C++ 03 se puede hacer esto con un poco de SFINAE para habilitar selectivamente diferentes versiones de la función de diferentes tipos:

#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); 
} 

Solía ​​impulso por conveniencia aquí, pero que podría rodar su poseer si el impulso no es una opción o usar C++ 11 en su lugar.

is_pod no es del todo lo que quiere en realidad, es probable que desee un rasgo is_container, pero that's not so trivial, you'll need to make a trait of your own y is_pod hace una buena aproximación para el uso de rasgos para habilitar selectivamente funciona como respuesta simple.

8

Usar un rasgo:

#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()); 
} 

Usted puede encontrar un rasgo adecuada en el pretty printer code.

0

Como escribieron mis predecesores, debes usar algún tipo de característica. Que es mejor usar Boost para eso, pero si usted no desea que sólo puede usar algo como esto (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); 
} 

No es una tarea fácil distinguir recipiente de matriz simple, por lo que se usa aquí revisando si el tipo tiene const_iterator. Probablemente también debería verificar si tiene begin(), end() y otras cosas que usaría en futuros códigos.

1

Puede probar las técnicas de Fracción de sustitución no es un error (SFINAE) aquí.

En primer lugar, se necesita una función para determinar si un tipo tiene un elemento iterador ...

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); 
}; 

En el código anterior, el estándar de C++ requiere test(typename U::const_iterator*) para ser utilizado con preferencia a la vaga" .. . "coincidencia de parámetros, siempre que Ues en realidad una estructura/clase con un tipo de miembro const_iterator. De lo contrario, tiene una "falla de sustitución", que no es un error fatal que detiene la compilación (de ahí SFINAE), y el intento de encontrar una función coincidente queda saturado por test(...). Como los tipos de devolución de las dos funciones son diferentes, el operador sizeof puede probar cuál ha sido emparejado, estableciendo el booleano result de manera apropiada.

A continuación, puede reenviar solicitudes para imprimir cosas a las especializaciones de plantilla que los apoyan ...

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: *" el operador 'sizeof' puede probar cuál se ha llamado" * - 'test()' nunca realmente llamado en absoluto. Ya lo sabes, estoy seguro. – cdhowie

+0

@cdhowie: punto justo - respuesta actualizada con "coincidente". Aclamaciones. –

Cuestiones relacionadas