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 obtenerQMap::iterator
como el tipo auto
: template<class Map>
struct RangeWrapper {
typedef typename Map::iterator MapIterator;
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;
};
}