c++ stl iterator istream-iterator

c++ - Limitando el rango para std:: copy con std:: istream_iterator



stl istream-iterator (3)

Como solicitó una solución que no es C ++ 0x, aquí hay una alternativa que usa std::generate_n y un funtor generador en lugar de std::copy_n e iteradores:

#include <algorithm> #include <string> #include <istream> #include <ostream> #include <iostream> template< typename ResultT, typename CharT = char, typename CharTraitsT = std::char_traits<CharT> > struct input_generator { typedef ResultT result_type; explicit input_generator(std::basic_istream<CharT, CharTraitsT>& input) : input_(&input) { } ResultT operator ()() const { // value-initialize so primitives like float // have a defined value if extraction fails ResultT v((ResultT())); *input_ >> v; return v; } private: std::basic_istream<CharT, CharTraitsT>* input_; }; template<typename ResultT, typename CharT, typename CharTraitsT> inline input_generator<ResultT, CharT, CharTraitsT> make_input_generator( std::basic_istream<CharT, CharTraitsT>& input ) { return input_generator<ResultT, CharT, CharTraitsT>(input); } int main() { float values[4]; std::generate_n(values, 4, make_input_generator<float>(std::cin)); std::cout << "Read exactly 4 floats" << std::endl; }

Si lo desea, puede usar este generador junto con boost::generator_iterator para usar el generador como un iterador de entrada.

He construido un ejemplo de trabajo mínimo para mostrar un problema que he encontrado al usar iteradores STL. Estoy usando istream_iterator para leer floats s (u otros tipos) de un std::istream :

#include <iostream> #include <iterator> #include <algorithm> int main() { float values[4]; std::copy(std::istream_iterator<float>(std::cin), std::istream_iterator<float>(), values); std::cout << "Read exactly 4 floats" << std::endl; // Not true! }

Esto lee todos los floats posibles, hasta EOF en values , que es de tamaño fijo, 4, por lo que ahora claramente quiero limitar el rango para evitar desbordamientos y leer exactamente / como máximo 4 valores.

Con más iteradores "normales" (es decir, RandomAccessIterator), siempre que begin+4 no haya pasado el final que harías:

std::copy(begin, begin+4, out);

Para leer exactamente 4 elementos.

¿Cómo se hace esto con std::istream_iterator ? La idea obvia es cambiar la llamada a std::copy para que sea:

std::copy(std::istream_iterator<float>(std::cin), std::istream_iterator<float>(std::cin)+4, values);

Pero (de manera bastante predecible) esto no se compila, no hay candidatos para el operator+ :

g++ -Wall -Wextra test.cc test.cc: In function ‘int main()’: test.cc:7: error: no match for ‘operator+’ in ‘std::istream_iterator<float, char, std::char_traits<char>, long int>(((std::basic_istream<char, std::char_traits<char> >&)(& std::cin))) + 4’

¿Alguna sugerencia? ¿Hay una forma correcta, "STLified" de pre-C ++ 0x para lograr esto? Obviamente, podría escribirlo como un bucle for, pero estoy buscando aprender algo sobre el STL aquí. Medio me preguntaba si abusaba de std::transform o std::merge etc. para lograr esta funcionalidad de alguna manera, pero no puedo ver cómo hacerlo.



Si no tiene disponible std::copy_n , es bastante fácil escribir el suyo propio:

namespace std_ext { template<class InputIterator, class Size, class OutputIterator> OutputIterator copy_n(InputIterator first, Size n, OutputIterator result) { for (int i=0; i<n; i++) { *result = *first; ++result; ++first; } return result; } }