59 votes

Itération sur une QMap avec for

J'ai une QMap et j'essaie d'écrire son contenu dans un fichier.

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

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

Pourquoi est-ce que je reçois : error: 'class QString' has no member named 'first' nor 'second'

Est e n'est pas du type QPair ?

60voto

Arlen Points 2967

Si vous voulez le style STL avec first y second Faites-le :

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

Si vous voulez utiliser ce que Qt offre, faites-le :

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

58voto

alexisdm Points 16064

Le C++11 range-based-for utilise le type de l'itérateur déréférencé comme type de "curseur" automatiquement déduit. Ici, il s'agit du type de l'expression *map.begin() .
Et puisque QMap::iterator::operator*() renvoie une référence à la valeur (de type QString & ), la clé n'est pas accessible par cette méthode.

Vous devez utiliser l'une des méthodes d'itération décrites dans la documentation, mais vous devez éviter d'utiliser l'option

  • keys() parce qu'il s'agit de créer une liste de clés et de rechercher la valeur de chaque clé, ou,
  • toStdMap() car elle copie tous les éléments de la carte dans une autre carte,

et ce ne serait pas très optimal.


Vous pouvez également utiliser un wrapper pour obtenir QMap::iterator en tant que auto type :

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

Il existe une autre enveloppe aquí .

33voto

Fezvez Points 3446

Pour les personnes intéressées par les optimisations, j'ai essayé plusieurs approches, j'ai fait quelques micro benchmarks, et je peux conclure que L'approche de type STL est nettement plus rapide .

J'ai essayé d'additionner des nombres entiers avec ces méthodes :

  • QMap::values()
  • Itérateur de type Java (comme indiqué dans la documentation)
  • itérateur de style STL (comme le conseille également la documentation)

Et je l'ai comparé à la somme des entiers d'une QList/QVector.

Résultats :

Reference vector :   244  ms
Reference list :     1239  ms

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

Code pour les personnes intéressées :

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

Edit Juillet 2017 : J'ai relancé ce code sur mon nouvel ordinateur portable (Qt 5.9, i7-7560U) et j'ai obtenu quelques changements intéressants.

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

Le style STL et le style Java ont des performances très similaires dans ce test de référence.

21voto

hmuelner Points 5311

QMap::iterator utilise key() et value() - que l'on peut trouver facilement dans la section documentation pour Qt 4.8 ou le documentation pour Qt-5 .

Editer :

Une boucle for basée sur une plage génère des codes similaires à celui-ci (voir Référence 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*() est équivalent à QMap::iterator::value(), et ne donne pas de paire.

La meilleure façon d'écrire cela est de ne pas utiliser de boucle for basée sur une plage :

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

13voto

Tim Meyer Points 5480

Dans l'"ancien" C++, en utilisant Qt, vous le feriez comme ceci :

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

Je n'ai pas de compilateur C++11 ici, mais peut-être que ce qui suit fonctionnera :

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

Vous pouvez également utiliser des itérateurs à la place, consultez le lien de hmuelners si vous préférez les utiliser.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X