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
?
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';
}
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, odertoStdMap()
, weil alle Kartenelemente in ein anderes kopiert werden,und das wäre nicht sehr optimal .
QMap::iterator
als auto
-Typ abzurufen: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';
}
Es gibt einen anderen Wrapper hier .
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:
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
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());
}
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
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;
};
}
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 Qt
s foreach
und c ++ 11):
QMap<QString, QString> extensions;
// ... fill extensions
foreach (const auto& value, extensions)
{
// to stuff with value
}