I've a QMap
object and I am trying to write its content to a file.
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'
Is e
not of type QPair
?
If you want the STL style with first
and second
, do this:
for(auto e : extensions.toStdMap()) { fout << e.first << "," << e.second << '\n'; }
If you want to use what Qt offers, do this:
for(auto e : extensions.keys()) { fout << e << "," << extensions.value(e) << '\n'; }
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.
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,and that wouldn't be very optimal.
QMap::iterator
as the auto
type: 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.
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.
I have tried adding integers with these methods :
And I have compared it with summing integers of a 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
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
QMap::iterator uses key() and value() - which can be found easily in the documentation for Qt 4.8 or the documentation for Qt-5.
Edit:
A range-based for loop generates codes similar to this (see CPP reference):
{
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.
The best way to write this is without range-based for loop:
auto end = extensions.cend();
for (auto it = extensions.cbegin(); it != end; ++it)
{
std::cout << qPrintable(it.key()) << "," << qPrintable(it.value());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With