c++ - ejemplo - ¿Por qué value_type/difference_type/pointer/reference de back_insert_iterator/front_insert_iterator/insert_iterator son todos vacíos?
iterator c++ example (4)
AFAIK, debería poder obtener container_type
del iterador, desde el cual debería poder obtener un value_type
. Es probable que desee especializarse para un pair
en algún momento. Esto debería responder a la segunda parte, como para la primera parte; no es seguro...
En mi proyecto, quiero dividir el flujo en algún tipo dado de valores, así que implemento una función de plantilla como
template <typename TElem, typename TOutputIter>
TOutputIter SplitSpace(std::istream& IS, TOutputIter result)
{
TElem elem;
while (IS >> elem)
{
*result = elem;
++result;
}
return result;
}
Creo que esto es incómodo ya que tengo que darle explícitamente el tipo de TElem
cuando lo llamo. Por ejemplo, tengo que escribir:
std::vector<int> v;
SplitSpace<int>(std::cin, back_inserter(v));
// I want to it to be SplitSpace(std::cin, back_inserter(v));
Traté de obtener el tipo de valor de un iterador (plantilla) y usé std::iterator_traits
siguiente manera:
template <typename TOutputIter>
TOutputIter SplitSpace(std::istream& IS, TOutputIter result)
{
typename std::iterator_traits<TOutputIter>::value_type elem;
while (IS >> elem)
{
*result = elem;
++result;
}
return result;
}
Sin embargo, los códigos anteriores no funcionan para back_insert_iterator
. Comprobé los códigos fuente de back_insert_iterator/front_insert_iterator/insert_iterator
en el std
nombres std
y encontré que value_type/difference_type/pointer/reference
son todos void
.
Me gustaría saber por qué estos tipos son todos void
, ¿hay alguna consideración para esto? Otra pregunta es: ¿es posible implementar la función SplitSpace
sin dar explícitamente el tipo de elemento cuando lo llame? Gracias.
Como mencionó Luc en los comentarios, lo que desea hacer se puede hacer fácilmente con la biblioteca estándar:
std::vector<int> v;
std::copy( std::istream_iterator( std::cin )
, std::istream_iterator()
, std::back_inserter( v ) );
En caso de que esté buscando una forma de evitar especificar el TElem
plantilla TElem
, puede usar este enfoque:
template <typename TOutputIter>
TOutputIter SplitSpace(std::istream& IS, TOutputIter result)
{
typedef typename TOutputIter::container_type::value_type TElem;
TElem elem;
while (IS >> elem)
{
*result = elem;
++result;
}
return result;
}
... por supuesto, dependiendo de los tipos de TOutputIter que pretenda usar, no debe usar este enfoque.
value_type
no tiene mucho sentido en el caso de OutputIterators, porque un iterador de salida no da acceso a ningún valor, y más importante aún puede aceptar una amplia gama de tipos de valores.
El único requisito para un OutputIterator es que debe soportar la expresión *it = o
donde o es un valor de algún tipo que está en el conjunto de tipos que se pueden escribir en el tipo de iterador particular de i (§24.2.1). Esto significa que un iterador de salida puede aceptar potencialmente una amplia gama de tipos: por ejemplo, su operator*
puede devolver un objeto proxy que sobrecarga operator=
para una variedad de tipos; ¿Qué debería ser value_type
en este caso?
Por ejemplo, considere el siguiente iterador de salida:
struct SwallowOutputIterator :
public std::iterator<output_iterator_tag, void, void, void, void>
{
struct proxy // swallows anything
{
template <typename T>
void operator=(const T &) {}
};
proxy operator*()
{
return proxy();
}
// increment operators
};
No hay una elección sensata para value_type
aquí.
El mismo razonamiento se aplica al pointer
y reference_type
. difference_type
no se define porque no se puede calcular la distancia entre dos iteradores de salida, ya que son single-pass.
NB: el estándar explícitamente establece que insert_iterator
y sus hermanos deben heredar del iterator<output_iterator_tag, void, void, void, void>
, por lo que esta no es una peculiaridad de su implementación.