C++ 使用 for 迭代 QMap

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8517853/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:42:14  来源:igfitidea点击:

Iterating over a QMap with for

c++qtc++11qmap

提问by user336635

I've a QMapobject and I am trying to write its content to a file.

我有一个QMap对象,我正在尝试将其内容写入文件。

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

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

Why do I get: error: 'class QString' has no member named 'first' nor 'second'

为什么我得到: error: 'class QString' has no member named 'first' nor 'second'

Is enot of type QPair?

e不是类型QPair

采纳答案by Arlen

If you want the STL style with firstand second, do this:

如果您想要带有firstand的 STL 样式second,请执行以下操作:

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

If you want to use what Qt offers, do this:

如果您想使用 Qt 提供的功能,请执行以下操作:

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

回答by alexisdm

C++11 range-based-for uses the type of the dereferenced iterator as the automatically deduced "cursor" type. Here, it is the type of the expression *map.begin().
And since QMap::iterator::operator*()returns a reference to the value (of type QString &), the key isn't accessible using that method.

C++11 range-based-for 使用解引用迭代器的类型作为自动推导的“游标”类型。在这里,它是表达式的类型*map.begin()
并且由于QMap::iterator::operator*()返回对值(类型QString &)的引用,因此无法使用该方法访问该键。

You should use one of the iterator methods described in the documentation but you should avoid using

您应该使用文档中描述的迭代器方法之一,但应避免使用

  • keys()because it involves creating a list of keys and then searching the value for each key, or,
  • toStdMap()because it copies all the map elements to another one,
  • keys()因为它涉及创建一个键列表,然后搜索每个键的值,或者,
  • toStdMap()因为它将所有地图元素复制到另一个元素,

and that wouldn't be very optimal.

这不会是非常理想的。



你也可以使用一个包装来获得QMap::iteratorQMap::iteratorautoauto类型:

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

There is another wrapper here.

还有另一种包装这里

回答by Fezvez

For people interested in optimizations, I have tried several approaches, did some micro benchmarks, and I can conclude that STL style approach is significantly faster.

对于对优化感兴趣的人,我尝试了几种方法,做了一些微基准测试,我可以得出结论,STL 风格的方法明显更快

I have tried adding integers with these methods :

我试过用这些方法添加整数:

  • QMap::values()
  • Java style iterator (as advised in the documentation)
  • STL style iterator (as advised in the documentation too)
  • QMap::values()
  • Java 风格的迭代器(如文档中所建议的)
  • STL 样式迭代器(也如文档中所述)

And I have compared it with summing integers of a QList/QVector

我将它与 QList/QVector 的整数相加进行了比较

Results :

结果 :

Reference vector :   244  ms
Reference list :     1239  ms

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

Code for those interested :

感兴趣的人的代码:

#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 July 2017 : I ran this code again on my new laptop (Qt 5.9, i7-7560U) and got some interesting changes

2017 年 7 月编辑:我在我的新笔记本电脑(Qt 5.9、i7-7560U)上再次运行此代码并进行了一些有趣的更改

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

STL style and Java style have very similar performances in this benchmark

STL 风格和 Java 风格在这个基准测试中的表现非常相似

回答by hmuelner

QMap::iterator uses key() and value() - which can be found easily in the documentation for Qt 4.8or the documentation for Qt-5.

QMap::iterator 使用 key() 和 value() - 这可以在Qt 4.8文档Qt-5文档中轻松找到。

Edit:

编辑:

A range-based for loop generates codes similar to this (see CPP reference):

基于范围的 for 循环生成与此类似的代码(请参阅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*() is equivalent to QMap::iterator::value(), and does not give a pair.

QMap::iterator::iterator*() 等价于 QMap::iterator::value(),并且不给出对。

The best way to write this is without range-based for loop:

最好的写法是不使用基于范围的 for 循环:

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

回答by Tim Meyer

In "old" C++, using Qt, you would do it like this:

在“旧”C++ 中,使用 Qt,你会这样做:

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

I don't have a C++11 compiler here but maybe the following will work:

我这里没有 C++11 编译器,但也许以下内容会起作用:

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

You can also use iterators instead, check out hmuelners link if you prefer using them

您也可以使用迭代器,如果您更喜欢使用它们,请查看 hmuelners 链接

回答by bcmpinc

Since Qt 5.10 you can use a simple wrapper class to use a range based for loop, but still be able to access both the key and value of the map entries.

从 Qt 5.10 开始,您可以使用简单的包装类来使用基于范围的 for 循环,但仍然能够访问映射条目的键和值。

Put the following code somewhere at the top of your source file or in a header that you include:

将以下代码放在源文件顶部或包含的标题中:

template<class K,class V>
struct QMapWrapper {
    const QMap<K,V>& map;
    QMapWrapper(const QMap<K,V>& map) : map(map) {}
    auto begin() { return map.keyValueBegin(); }
    auto end()   { return map.keyValueEnd();   }
};

To iterate over all entries you can simply write:

要遍历所有条目,您可以简单地编写:

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

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

The type of ewill be std::pair<const QString&, const QString&>as is partially specified in the QKeyValueIterator documentation.

的类型的e将被std::pair<const QString&, const QString&>作为在被指定的部分QKeyValueIterator文档

Considering that the pair uses references, removing the const qualifier for map might allow you to modify values in the map by assigning to e.second. I haven't tested this though.

考虑到该对使用引用,删除 map 的 const 限定符可能允许您通过分配给 来修改映射中的值e.second。不过我还没有测试过这个。

I'm using a reference for map as this avoids calling the copy constructor and hopefully allows the compiler to optimize the QMapWrapper class away.

我正在使用 map 的引用,因为这避免了调用复制构造函数,并希望允许编译器优化 QMapWrapper 类。



The above example uses class template argument deduction, which was introduced in C++17. If you're using an older standard, the template parameters for QMapWrapper must be specified when calling the constructor. In this case a factory method might be useful:

上面的例子使用了 C++17 中引入的类模板参数推导。如果您使用的是旧标准,则在调用构造函数时必须指定 QMapWrapper 的模板参数。在这种情况下,工厂方法可能很有用:

template<class K,class V>
QMapWrapper<K,V> wrapQMap(const QMap<K,V>& map) {
    return QMapWrapper<K,V>(map);
}

This is used as:

这用作:

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

回答by Thierry Joel

I used something like this, to achieve my own result. Just in case someone needed the keys and values separately.

我用这样的东西来达到我自己的结果。以防万一有人需要单独的键和值。

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

回答by DomTomCat

Another convenient method, from the QMap Docs. It allows explicit access to key and value (Java-Style iterator):

另一种方便的方法,来自QMap Docs。它允许显式访问键和值(Java 风格的迭代器):

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

In case you want to be able to overwrite, use QMutableMapIteratorinstead.

如果您希望能够覆盖,请QMutableMapIterator改用。

There's another convenient Qtmethod, if you're only interested in reading the values, without the keys (using Qts foreachand c++11):

还有另一种方便的Qt方法,如果您只对读取值感兴趣,而没有键(使用Qtsforeach和 c++11):

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