que libreria iterators iteradores iterador assigning c++ stl iterator

c++ - libreria - Tipo de valor del iterador de salida



list iterator c++ (2)

El STL comúnmente define un iterador de salida así:

template<class Cont> class insert_iterator : public iterator<output_iterator_tag,void,void,void,void> { // ...

¿Por qué los iteradores de salida definen value_type como void ? Sería útil para un algoritmo saber qué tipo de valor se supone debe generar.

Por ejemplo, una función que traduce una consulta de URL "key1=value1&key2=value2&key3=value3" en cualquier contenedor que contenga elementos de cadena de valor-clave.

template<typename Ch,typename Tr,typename Out> void parse(const std::basic_string<Ch,Tr>& str, Out result) { std::basic_string<Ch,Tr> key, value; // loop over str, parse into p ... *result = typename iterator_traits<Out>::value_type(key, value); }

La página de referencia de SGI de value_type insinúa que esto se debe a que no es posible desreferenciar un iterador de salida. Pero ese no es el único uso de value_type : podría querer crear una instancia para asignarlo al iterador.

¿Qué enfoque alternativo existe para construir un valor a la salida con el iterador de salida? Dos enfoques que consideré:

  • Acepte un parámetro de functor que devolvería un objeto del tipo correcto. Todavía quiero tener una versión del algoritmo que no tome ese parámetro de objeto de función.
  • Requerir que el contenedor de salida contenga el pair<string,string> , o bien un tipo convertible a partir de eso. Me pregunto si puedo prescindir de este requisito, quizás permitir cualquier elemento que pueda construir a partir de dos std::string s.

El tipo de valor real del iterador bien podría ser el iterador mismo. operator* puede simplemente devolver una referencia a *this porque el trabajo real lo realiza el operador de asignación. Puede encontrar que *it = x; y it = x; tienen exactamente el mismo efecto con los iteradores de salida (supongo que se pueden tomar medidas especiales para evitar que estos últimos compilen).

Como tal, definir el tipo de valor real sería tan inútil. Definirlo como un void , por otro lado, puede prevenir errores como:

typename Iter::value_type v = *it; //useless with an output iterator if it compiled

Supongo que este es solo el límite del concepto de iteradores de salida: son objetos que "abusan" de la sobrecarga del operador, para que parezca puntero, mientras que en realidad algo completamente diferente está sucediendo.

Tu problema es interesante, sin embargo. Si desea admitir cualquier contenedor, los iteradores de salida en cuestión probablemente sean std::insert_iterator , std::front_insert_iterator y std::back_insert_iterator . En este caso, podría hacer algo como lo siguiente:

#include <iterator> #include <vector> #include <string> #include <map> #include <iostream> //Iterator has value_type, use it template <class T, class IterValue> struct value_type { typedef IterValue type; }; //output iterator, use the container''s value_type template <class Container> struct value_type<Container, void> { typedef typename Container::value_type type; }; template <class T, class Out> void parse_aux(Out out) { *out = typename value_type<T, typename Out::value_type>::type("a", "b"); } template <template <class> class Out, class T> void parse(Out<T> out) { parse_aux<T>(out); } //variadic template in C++0x could take care of this and other overloads that might be needed template <template <class, class> class Out, class T, class U> void parse(Out<T, U> out) { parse_aux<T>(out); } int main() { std::vector<std::pair<std::string, std::string> > vec; parse(std::back_inserter(vec)); std::cout << vec[0].first << '' '' << vec[0].second << ''/n''; std::map<std::string, std::string> map; parse(std::inserter(map, map.end())); std::cout << map["a"] << ''/n''; //just might also support normal iterators std::vector<std::pair<std::string, std::string> > vec2(1); parse(vec2.begin()); std::cout << vec2[0].first << '' '' << vec2[0].second << ''/n''; }

Todavía te llegaría tan lejos. Supongo que uno podría llevar esto más allá, por lo que también puede administrar, por ejemplo, std::ostream_iterator<printable_type> , pero en algún punto se volvería tan complejo que se necesita un dios para descifrar los mensajes de error, si algo sale mal.


El propósito de value_type de un iterador es definir el tipo que se devuelve cuando se elimina la referencia de ese iterador. Para los iteradores de salida, el único uso legítimo del operador de desreferencia es cuando se usa junto con el operador de asignación, en forma de *output_iterator = value . El tipo que se devuelve al desreferenciar un iterador de salida no tiene necesariamente ninguna relación directa con los tipos que se pueden almacenar a través del iterador de salida. La única relación necesaria es que haya alguna forma de asignar los últimos tipos al primer tipo.

Además, el iterador de salida puede almacenar valores de múltiples tipos, y estos tipos no tienen que tener ninguna relación entre sí. Tomemos como ejemplo el null_output_iterator descrito en Descartar la salida de una función que necesita un iterador de salida . Ese iterador puede aceptar para el almacenamiento cualquier tipo de valor.