c++ qt c++11 qmap

c++ - Iterando un QMap con



qt c++11 (7)

Tengo un objeto QMap y estoy tratando de escribir su contenido en un archivo.

QMap<QString, QString> extensions; //.. for(auto e : extensions) { fout << e.first << "," << e.second << ''/n''; }

¿Por qué obtengo: error: ''class QString'' has no member named ''first'' nor ''second''

¿Es e no del tipo QPair ?


C ++ 11 basado en rango utiliza el tipo de iterador sin referencias como el tipo de "cursor" deducido automáticamente. Aquí, es el tipo de la expresión *map.begin() .
Y dado que QMap::iterator::operator*() devuelve una referencia al valor (de tipo QString & ), no se puede acceder a la clave utilizando ese método.

Debe usar uno de los métodos de iteración descritos en la documentación pero debe evitar el uso

  • keys() porque implica crear una lista de claves y luego buscar el valor de cada clave, o
  • toStdMap() porque copia todos los elementos del mapa a otro,

y eso no sería muy óptimo.

También podría usar un contenedor para obtener QMap::iterator como el tipo auto :

template<class Map> struct RangeWrapper { typedef typename Map::iterator MapIterator; Map &map; RangeWrapper(Map & map_) : map(map_) {} struct iterator { MapIterator mapIterator; iterator(const MapIterator &mapIterator_): mapIterator(mapIterator_) {} MapIterator operator*() { return mapIterator; } iterator & operator++() { ++mapIterator; return *this; } bool operator!=(const iterator & other) { return this->mapIterator != other.mapIterator; } }; iterator begin() { return map.begin(); } iterator end() { return map.end(); } }; // Function to be able to use automatic template type deduction template<class Map> RangeWrapper<Map> toRange(Map & map) { return RangeWrapper<Map>(map); } // Usage code QMap<QString, QString> extensions; ... for(auto e : toRange(extensions)) { fout << e.key() << "," << e.value() << ''/n''; }

Hay otro envoltorio here .


En C ++ "antiguo", usando Qt, lo harías así:

QMap< QString, whatever > extensions; //... foreach( QString key, extensions.keys() ) { fout << key << "," << extensions.value( key ) << ''/n''; }

No tengo un compilador de C ++ 11 aquí, pero quizás lo siguiente funcione:

for( auto key: extensions.keys() ) { fout << key << "," << extensions.value( key ) << ''/n''; }

También puedes usar iteradores, echa un vistazo al enlace hmuelners si prefieres usarlos


Otro método conveniente, de los Documentos de QMap . Permite el acceso explícito a la clave y el valor (iterador de estilo Java):

QMap<QString, QString> extensions; // ... fill extensions QMapIterator<QString, QString> i(extensions); while (i.hasNext()) { i.next(); qDebug() << i.key() << ": " << i.value(); }

En caso de que desee sobrescribir, use QMutableMapIterator .

Hay otro método conveniente de Qt , si solo está interesado en leer los valores, sin las claves (usando Qt s foreach y c ++ 11):

QMap<QString, QString> extensions; // ... fill extensions foreach (const auto& value, extensions) { // to stuff with value }


Para las personas interesadas en las optimizaciones, he intentado varios enfoques, hice algunos micro benchmarks, y puedo concluir que el enfoque de estilo STL es significativamente más rápido .

He intentado agregar enteros con estos métodos:

  • QMap :: values ​​()
  • Iterador de estilo Java (como se recomienda en la documentación)
  • Iterador de estilo STL (como se aconseja en la documentación también)

Y lo he comparado con sumando enteros de un QList / QVector

Resultados:

Reference vector : 244 ms Reference list : 1239 ms QMap::values() : 6504 ms Java style iterator : 6199 ms STL style iterator : 2343 ms

Código para los interesados:

#include <QDateTime> #include <QMap> #include <QVector> #include <QList> #include <QDebug> void testQMap(){ QMap<int, int> map; QVector<int> vec; QList<int> list; int nbIterations = 100; int size = 1000000; volatile int sum = 0; for(int i = 0; i<size; ++i){ int randomInt = qrand()%128; map[i] = randomInt; vec.append(randomInt); list.append(randomInt); } // Rererence vector/list qint64 start = QDateTime::currentMSecsSinceEpoch(); for(int i = 0; i<nbIterations; ++i){ sum = 0; for(int j : vec){ sum += j; } } qint64 end = QDateTime::currentMSecsSinceEpoch(); qDebug() << "Reference vector : /t" << (end-start) << " ms"; qint64 startList = QDateTime::currentMSecsSinceEpoch(); for(int i = 0; i<nbIterations; ++i){ sum = 0; for(int j : list){ sum += j; } } qint64 endList = QDateTime::currentMSecsSinceEpoch(); qDebug() << "Reference list : /t" << (endList-startList) << " ms"; // QMap::values() qint64 start0 = QDateTime::currentMSecsSinceEpoch(); for(int i = 0; i<nbIterations; ++i){ sum = 0; QList<int> values = map.values(); for(int k : values){ sum += k; } } qint64 end0 = QDateTime::currentMSecsSinceEpoch(); qDebug() << "QMap::values() : /t" << (end0-start0) << " ms"; // Java style iterator qint64 start1 = QDateTime::currentMSecsSinceEpoch(); for(int i = 0; i<nbIterations; ++i){ sum = 0; QMapIterator<int, int> it(map); while (it.hasNext()) { it.next(); sum += it.value(); } } qint64 end1 = QDateTime::currentMSecsSinceEpoch(); qDebug() << "Java style iterator : /t" << (end1-start1) << " ms"; // STL style iterator qint64 start2 = QDateTime::currentMSecsSinceEpoch(); for(int i = 0; i<nbIterations; ++i){ sum = 0; QMap<int, int>::const_iterator it = map.constBegin(); auto end = map.constEnd(); while (it != end) { sum += it.value(); ++it; } } qint64 end2 = QDateTime::currentMSecsSinceEpoch(); qDebug() << "STL style iterator : /t" << (end2-start2) << " ms"; qint64 start3 = QDateTime::currentMSecsSinceEpoch(); for(int i = 0; i<nbIterations; ++i){ sum = 0; auto end = map.cend(); for (auto it = map.cbegin(); it != end; ++it) { sum += it.value(); } } qint64 end3 = QDateTime::currentMSecsSinceEpoch(); qDebug() << "STL style iterator v2 : /t" << (end3-start3) << " ms"; }

Edición de julio de 2017: volví a ejecutar este código en mi nueva computadora portátil (Qt 5.9, i7-7560U) y obtuve algunos cambios interesantes

Reference vector : 155 ms Reference list : 157 ms QMap::values(): 1874 ms Java style iterator: 1156 ms STL style iterator: 1143 ms

El estilo STL y el estilo Java tienen desempeños muy similares en este punto de referencia


QMap :: iterator usa key () y value () - que se pueden encontrar fácilmente en la documentación de Qt 4.8 o en la documentación de Qt-5 .

Editar:

Un bucle for basado en rango genera códigos similares a este (ver referencia de CPP ):

{ for (auto __begin = extensions.begin(), __end = extensions.end(); __begin != __end; ++__begin) { auto e = *__begin; // <--- this is QMap::iterator::operator*() fout << e.first << "," << e.second << ''/n''; } }

QMap :: iterator :: iterator * () es equivalente a QMap :: iterator :: value (), y no da un par.

La mejor forma de escribir esto es sin bucle basado en rango:

auto end = extensions.cend(); for (auto it = extensions.cbegin(); it != end; ++it) { std::cout << qPrintable(it.key()) << "," << qPrintable(it.value()); }


Si desea el estilo STL con el first y el second , haga esto:

for(auto e : extensions.toStdMap()) { fout << e.first << "," << e.second << ''/n''; }

Si quieres usar lo que Qt ofrece, haz esto:

for(auto e : extensions.keys()) { fout << e << "," << extensions.value(e) << ''/n''; }


Usé algo como esto, para lograr mi propio resultado. Solo en caso de que alguien necesite las claves y los valores por separado.

{ QMap<int,string> map; map.insert(1,"One"); map.insert(2,"Two"); map.insert(3,"Three"); map.insert(4,"Four"); fout<<"Values in QMap ''map'' are:"<<endl; foreach(string str,map) { cout<<str<<endl; }; fout<<"Keys in QMap ''map'' are:"<<endl; foreach(int key,map.keys()) { cout<<key<<endl; }; }