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
Iterating over a QMap with for
提问by user336635
I've a QMap
object 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 e
not of type QPair
?
是e
不是类型QPair
?
采纳答案by Arlen
If you want the STL style with first
and second
, do this:
如果您想要带有first
and的 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::iterator
QMap::iterator
的auto
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';
}
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 e
will 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 QMutableMapIterator
instead.
如果您希望能够覆盖,请QMutableMapIterator
改用。
There's another convenient Qt
method, if you're only interested in reading the values, without the keys (using Qt
s foreach
and c++11):
还有另一种方便的Qt
方法,如果您只对读取值感兴趣,而没有键(使用Qt
sforeach
和 c++11):
QMap<QString, QString> extensions;
// ... fill extensions
foreach (const auto& value, extensions)
{
// to stuff with value
}