iterador español c++ templates stl containers

español - plantillas simples de C++ adaptadas para STL Containers



iterador c++ (6)

Necesito una plantilla como esta, que funcione a la perfección

template <typename container> void mySuperTempalte (const container myCont) { //do something here }

entonces quiero especializar la plantilla anterior para std :: string, así que se me ocurrió

template <typename container> void mySuperTempalte (const container<std::string> myCont) { //check type of container //do something here }

que no funciona, y arroja un error. Me gustaría hacer que funcione el segundo ejemplo y luego, si es posible, me gustaría agregar algún código en la plantilla para verificar si se usó std :: vector / std :: deque / std :: list, para hacer algo diferente en cada caso. Así que usé plantillas porque el 99% del código es el mismo tanto para vectores como para deques, etc.


¿Has probado un parámetro de tipo de letra de plantilla? La sintaxis es un poco extraña porque emula la sintaxis utilizada para declarar dicho contenedor. Hay un buen artículo de InformIT explicando esto con más detalle.

template <template <typename> class Container> void mySuperTemplate(Container<std::string> const& cont) { }

¡Observe que también debe declarar el argumento como una referencia!

Por cierto: este comentario

//check type of container

es un claro indicio de que estás haciendo algo mal. No desea verificar el tipo de contenedor. Sobrecarga de usuario más sofisticada, como se muestra en la respuesta de sep.


@sep

''Solución simple

La respuesta publicada por ''sep'' es bastante buena, probablemente lo suficientemente buena para el 99% de los desarrolladores de aplicaciones, pero podría usar alguna mejora si es parte de una interfaz de biblioteca, para repetir:

Para especializarse en vectores:

template<typename C> void mySuperTempalte (std::vector<C> myCont) { //check type of container //do something here }

Esto funcionará siempre que la persona que llama no esté usando std :: vector. Si esto funciona lo suficientemente bien para ti, para especializarte en vectores, listas, etc., entonces detente aquí y solo usa eso.

Una solución más completa

En primer lugar, tenga en cuenta que no puede especializar parcialmente las plantillas de funciones; puede crear sobrecargas. Y si dos o más de ellos coinciden en el mismo grado, obtendrá errores de "sobrecarga ambiciosa". Así que tenemos que hacer exactamente una coincidencia en cada caso que desee apoyar.

Una técnica para hacer esto es usar la técnica enable_if - enable_if le permite tomar selectivamente sobrecargas de plantilla de función fuera de la posible lista de coincidencias usando una regla de lenguaje oscura ... básicamente, si alguna expresión booleana es falsa, la sobrecarga se vuelve ''invisible'' . Busque SFINAE para obtener más información si tiene curiosidad.

Ejemplo. Este código se puede compilar desde la línea de comandos con MinGW (g ++ parameterize.cpp) o VC9 (cl / EHsc parameterize.cpp) sin error:

#include <iostream> #include <vector> #include <string> using namespace std; template <bool B, class T> struct enable_if {}; template <class T> struct enable_if<true, T> { typedef T type; }; template <class T, class U> struct is_same { enum { value = false }; }; template <class T> struct is_same<T,T> { enum { value = true }; }; namespace detail{ // our special function, not for strings // use ... to make it the least-prefered overload template <class Container> void SpecialFunction_(const Container& c, ...){ cout << "invoked SpecialFunction() default/n"; } // our special function, first overload: template <class Container> // enable only if it is a container of mutable strings typename enable_if< is_same<typename Container::value_type, string>::value, void >::type SpecialFunction_(const Container& c, void*){ cout << "invoked SpecialFunction() for strings/n"; } } // wrapper function template <class Container> void SpecialFunction(const Container& c){ detail::SpecialFunction_(c, 0); } int main(){ vector<int> vi; cout << "calling with vector<int>/n"; SpecialFunction(vi); vector<string> vs; cout << "/ncalling with vector<string>/n"; SpecialFunction(vs); }

Salida:

d:/scratch>parameterize.exe calling with vector<int> invoked SpecialFunction() default calling with vector<string> invoked SpecialFunction() for strings d:/scratch>


Especializarse:

template<> void mySuperTempalte<std:string>(const std::string myCont) { //check type of container //do something here }

Para especializarse en vectores:

template<typename C> void mySuperTempalte (std::vector<C> myCont) { //check type of container //do something here }

Para especializarse en deque:

template<typename C> void mySuperTempalte (std::deque<C> myCont) { //check type of container //do something here }


Las respuestas hasta ahora parecen útiles, pero creo que usaría una construcción diferente. Espero que todos los contenedores definan value_type, al igual que los contenedores STL. Por lo tanto, puedo escribir

inline template <typename C> void mySuperTemplate (C const& myCont) { mySuperTemplateImpl<C, typename C::value_type>(myCont); }

En general, es más fácil actuar sobre un parámetro que ha extraído explícitamente.


Si entiendo tu problema correctamente, tienes un algoritmo que funcionará para el vector de contenedores STL, deque, etc. pero está intentando escribir una especialización de plantilla para la cadena. Si este es el caso, entonces puede escribir el método de plantilla generalizado que definió en su pregunta:

template<typename container> void mySuperTempalte( const container &myCont ) { // Implement STL container code }

Luego, para su especialización en cuerdas, declara:

template<> void mySuperTempalte( const container<std::string> &myCont ) { // Implement the string code }

Para cualquier otra especialización simplemente cambie la declaración de tipo para myCont. Si realmente necesita hacer esto para los contenedores vector y deque, entonces haga que el parámetro template sea el parámetro para el tipo en ese contenedor en lugar del contenedor en sí, como lo sugirió Sep.

template<typename C> void mySuperTempalte( const std::vector<C> &myCont) { // check type of container // do something here }

Vale la pena tratar de evitar esto haciendo que su primera implementación funcione con todos los contenedores STL para hacer su vida más fácil, entonces solo necesita la especialización para la clase de cadena. Incluso considere convertir su cadena en un vector para evitar la especialización en conjunto.

En una nota al margen, he cambiado el parámetro de contenedor a una referencia constante, supongo que esto es lo que quieres, ya que declaras el objeto const de todos modos, de esta manera evitas una copia.


Si se trata de un buen diseño o no, queda en discusión. De todos modos, puede detectar el tipo de contenedor utilizando especializaciones de plantillas parciales. En particular:

enum container_types { unknown, list_container, vector_container }; template <typename T> struct detect_container_ { enum { type = unknown }; }; template <typename V> struct detect_container_< std::vector<V> > // specialization { enum { type = vector_container }; }; template <typename V> struct detect_container_< std::list<V> > { enum { type = list_container }; }; // Helper function to ease usage template <typename T> container_types detect_container( T const & ) { return static_cast<container_types>( detect_container_<T>::type ); } int main() { std::vector<int> v; assert( detect_container( v ) == vector_container ); }