c++ - resueltos - imprimir un vector en c
Encontrar la posición del elemento en C++ 11 en función del rango para el ciclo (7)
Supongamos que tengo el siguiente código:
vector<int> list;
for(auto& elem:list) {
int i = elem;
}
¿Puedo encontrar la posición de elem
en el vector sin mantener un iterador por separado?
Hay una manera sorprendentemente simple de hacer esto
vector<int> list;
for(auto& elem:list) {
int i = (&elem-&*(list.begin()));
}
donde seré su índice requerido.
Esto aprovecha el hecho de que los vectores C ++ son siempre contiguos .
Leí de sus comentarios que una de las razones por las que desea saber el índice es saber si el elemento es el primero / último en la secuencia. Si es así, puedes hacer
for(auto& elem:list) {
// loop code ...
if(&elem == &*std::begin(list)){ ... special code for first element ... }
if(&elem == &*std::prev(std::end(list))){ ... special code for last element ... }
// if(&elem == &*std::rbegin(list)){... (C++14 only) special code for last element ...}
// loop code ...
}
EDITAR: Por ejemplo, esto imprime un contenedor omitiendo un separador en el último elemento. Funciona para la mayoría de los contenedores que puedo imaginar (incluidas las matrices), (demostración en línea http://coliru.stacked-crooked.com/a/9bdce059abd87f91 ):
#include <iostream>
#include <vector>
#include <list>
#include <set>
using namespace std;
template<class Container>
void print(Container const& c){
for(auto& x:c){
std::cout << x;
if(&x != &*std::prev(std::end(c))) std::cout << ", "; // special code for last element
}
std::cout << std::endl;
}
int main() {
std::vector<double> v{1.,2.,3.};
print(v); // prints 1,2,3
std::list<double> l{1.,2.,3.};
print(l); // prints 1,2,3
std::initializer_list<double> i{1.,2.,3.};
print(i); // prints 1,2,3
std::set<double> s{1.,2.,3.};
print(s); // print 1,2,3
double a[3] = {1.,2.,3.}; // works for C-arrays as well
print(a); // print 1,2,3
}
No, no puedes (al menos, no sin esfuerzo). Si necesita la posición de un elemento, no debe usar el rango para. Recuerde que es solo una herramienta de conveniencia para el caso más común: ejecutar algún código para cada elemento. En las circunstancias menos comunes en las que necesita la posición del elemento, debe usar el ciclo regular for
menos conveniente.
Sí, puedes, solo toma algunos masajes;)
El truco es usar la composición: en lugar de iterar sobre el contenedor directamente, lo "zip" con un índice en el camino.
Código de cremallera especializado:
template <typename T>
struct iterator_extractor { typedef typename T::iterator type; };
template <typename T>
struct iterator_extractor<T const> { typedef typename T::const_iterator type; };
template <typename T>
class Indexer {
public:
class iterator {
typedef typename iterator_extractor<T>::type inner_iterator;
typedef typename std::iterator_traits<inner_iterator>::reference inner_reference;
public:
typedef std::pair<size_t, inner_reference> reference;
iterator(inner_iterator it): _pos(0), _it(it) {}
reference operator*() const { return reference(_pos, *_it); }
iterator& operator++() { ++_pos; ++_it; return *this; }
iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }
bool operator==(iterator const& it) const { return _it == it._it; }
bool operator!=(iterator const& it) const { return !(*this == it); }
private:
size_t _pos;
inner_iterator _it;
};
Indexer(T& t): _container(t) {}
iterator begin() const { return iterator(_container.begin()); }
iterator end() const { return iterator(_container.end()); }
private:
T& _container;
}; // class Indexer
template <typename T>
Indexer<T> index(T& t) { return Indexer<T>(t); }
Y usándolo:
#include <iostream>
#include <iterator>
#include <limits>
#include <vector>
// Zipper code here
int main() {
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto p: index(v)) {
std::cout << p.first << ": " << p.second << "/n";
}
}
Puedes verlo en ideone , aunque carece del soporte de lazo para rango, por lo que es menos bonito.
EDITAR:
Recordaba que debería consultar Boost.Range con más frecuencia. Desafortunadamente no hay rango de zip
, pero encontré un perl: boost::adaptors::indexed
. Sin embargo, requiere acceso al iterador para extraer el índice. Vergüenza: x
De lo contrario, con el counting_range
y un zip
genérico estoy seguro de que podría ser posible hacer algo interesante ...
En el mundo ideal, me imagino:
int main() {
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto tuple: zip(iota(0), v)) {
std::cout << tuple.at<0>() << ": " << tuple.at<1>() << "/n";
}
}
Con zip
crea automáticamente una vista como un rango de tuplas de referencias y iota(0)
simplemente creando un rango "falso" que comienza desde 0
y solo cuenta hacia el infinito (o bueno, el máximo de su tipo ...).
Si insiste en usar el rango basado en, y para conocer el índice, es bastante trivial mantener el índice como se muestra a continuación. No creo que haya una solución más simple / más limpia para el rango para bucles. Pero, ¿por qué no utilizar un estándar para (;;)? Eso probablemente haría que tu intento y tu código fueran más claros.
vector<int> list;
int idx = 0;
for(auto& elem:list) {
int i = elem;
//TODO whatever made you want the idx
++idx;
}
Si tiene un compilador compatible con C ++ 14, puede hacerlo en un estilo funcional:
#include <iostream>
#include <string>
#include <vector>
#include <functional>
template<typename T>
void for_enum(T& container, std::function<void(int, typename T::value_type&)> op)
{
int idx = 0;
for(auto& value : container)
op(idx++, value);
}
int main()
{
std::vector<std::string> sv {"hi", "there"};
for_enum(sv, [](auto i, auto v) {
std::cout << i << " " << v << std::endl;
});
}
Funciona con clang 3.4 y gcc 4.9 (no con 4.8); para ambos es necesario establecer -std=c++1y
. La razón por la que necesita c ++ 14 es debido a los parámetros auto
en la función lambda.
jrok tiene razón: los bucles basados en rangos no están diseñados para ese propósito.
Sin embargo, en su caso, es posible calcularlo usando aritmética de puntero ya que el vector
almacena sus elementos contiguamente (*)
vector<int> list;
for(auto& elem:list) {
int i = elem;
int pos = &elem-&list[0]; // pos contains the position in the vector
// also a &-operator overload proof alternative (thanks to ildjarn) :
// int pos = addressof(elem)-addressof(list[0]);
}
Pero esta es claramente una mala práctica ya que ofusca el código y lo hace más frágil (se rompe fácilmente si alguien cambia el tipo de contenedor, sobrecarga el operador &
o reemplaza ''auto &'' por ''auto''. ¡Buena suerte para depurar eso!)
NOTA: La contigüidad está garantizada para el vector en C ++ 03, y la matriz y la cadena en el estándar C ++ 11.