webentwicklung-frage-antwort-db.com.de

Iteration über eine QMap mit for

Ich habe ein QMap-Objekt und versuche, den Inhalt in eine Datei zu schreiben.

QMap<QString, QString> extensions;
//.. 

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

Warum bekomme ich: error: 'class QString' has no member named 'first' nor 'second'

Ist e nicht vom Typ QPair?

44
user336635

Wenn Sie den STL-Stil mit first und second verwenden möchten, führen Sie folgende Schritte aus:

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

Wenn Sie die Angebote von Qt nutzen möchten, gehen Sie folgendermaßen vor:

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

C++ 11-bereichsbasiert-for verwendet den Typ des dereferenzierten Iterators als automatisch abgeleiteten "Cursor" -Typ. Hierbei handelt es sich um den Typ des Ausdrucks *map.begin().
Und da QMap::iterator::operator*() einen Verweis auf den Wert (vom Typ QString &) zurückgibt, ist der Schlüssel mit dieser Methode nicht zugänglich.

Sie sollten eine der in der Dokumentation beschriebenen Iterator-Methoden verwenden. Sie sollten jedoch die Verwendung vermeiden 

  • keys(), da es sich um das Erstellen einer Liste von Schlüsseln handelt und der Wert für jeden Schlüssel gesucht wird, oder
  • toStdMap(), weil alle Kartenelemente in ein anderes kopiert werden,

und das wäre nicht sehr optimal .


Sie können auch einen Wrapper verwenden, um QMap::iterator als auto-Typ abzurufen:

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';
}

Es gibt einen anderen Wrapper hier .

40
alexisdm

Für Optimierungsinteressierte habe ich verschiedene Ansätze ausprobiert, einige Mikro-Benchmarks erstellt und kann zu dem Schluss kommen, dass der STL-Ansatz wesentlich schneller ist.

Ich habe versucht, Ganzzahlen mit diesen Methoden hinzuzufügen:

  • QMap :: values ​​()
  • Java-Stil-Iterator (wie in der Dokumentation empfohlen)
  • STL-Stil-Iterator (wie auch in der Dokumentation empfohlen)

Und ich habe es mit summierenden Ganzzahlen einer QList/QVector verglichen

Ergebnisse : 

Reference vector :   244  ms
Reference list :     1239  ms

QMap::values() :     6504  ms
Java style iterator :    6199  ms
STL style iterator :     2343  ms

Code für Interessierte: 

#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";
}

Juli 2017 bearbeiten: Ich habe diesen Code erneut auf meinem neuen Laptop (Qt 5.9, i7-7560U) ausgeführt und einige interessante Änderungen erhalten

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

Der STL-Stil und der Java-Stil weisen in diesem Benchmark sehr ähnliche Leistungen auf

22
Fezvez

QMap :: iterator verwendet key () und value (), die in der Dokumentation für Qt 4.8 oder der Dokumentation für Qt-5 leicht zu finden sind.

Bearbeiten:

Eine bereichsbasierte for-Schleife generiert ähnliche Codes (siehe CPP-Referenz ):

{
    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 * () entspricht QMap :: iterator :: value () und gibt kein Paar aus.

Der beste Weg, dies zu schreiben, ist ohne bereichsbasierte Schleife:

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

In "altem" C++ würden Sie dies mit Qt folgendermaßen tun:

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

Ich habe keinen C++ 11-Compiler hier, aber vielleicht wird Folgendes funktionieren:

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

Sie können stattdessen auch Iteratoren verwenden. Schauen Sie sich den Link zu hmuelners an, wenn Sie sie bevorzugen

12
Tim Meyer

Ich habe so etwas verwendet, um mein eigenes Ergebnis zu erzielen. Nur für den Fall, dass jemand die Schlüssel und Werte separat benötigt.

{
   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;
   }; 
}  
1
Thierry Joel

Eine andere bequeme Methode aus dem QMap Docs . Es ermöglicht den expliziten Zugriff auf Schlüssel und Werte (Java-Style-Iterator):

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

Wenn Sie überschreiben möchten, verwenden Sie stattdessen QMutableMapIterator.

Es gibt eine andere praktische Qt-Methode, wenn Sie nur die Werte lesen möchten, ohne die Schlüssel (mit Qts foreach und c ++ 11):

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