J'ai un objet 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 je reçois: error: 'class QString' has no member named 'first' nor 'second'
e
n'est-il pas de type QPair
?
Si vous voulez le style STL avec first
et second
, procédez comme suit:
for(auto e : extensions.toStdMap())
{
fout << e.first << "," << e.second << '\n';
}
Si vous souhaitez utiliser les offres Qt, procédez comme suit:
for(auto e : extensions.keys())
{
fout << e << "," << extensions.value(e) << '\n';
}
C++ 11 range-based-for utilise le type de l'itérateur déréférencé comme type de "curseur" déduit automatiquement. Ici, c'est le type de l'expression *map.begin()
.
Et comme QMap::iterator::operator*()
renvoie une référence à la valeur (de type QString &
), la clé n’est pas accessible à l’aide de cette méthode.
Vous devez utiliser l'une des méthodes d'itérateur décrites dans la documentation, mais vous devez éviter d'utiliser
keys()
car il s’agit de créer une liste de clés, puis de rechercher la valeur de chaque clé, outoStdMap()
car il copie tous les éléments de la carte dans un autre,et ce ne serait pas très optimal .
QMap::iterator
en tant que type 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';
}
Il y a un autre wrapper ici .
Pour les personnes intéressées par les optimisations, j'ai essayé plusieurs approches, quelques micro-points de repère et je peux conclure que l'approche de style STL est nettement plus rapide .
J'ai essayé d'ajouter des entiers avec ces méthodes:
Et je l'ai comparé avec la somme des nombres entiers d'un 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 intéressés:
#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";
}
Éditer juillet 2017: J'ai de nouveau exécuté ce code sur mon nouvel ordinateur portable (Qt 5.9, i7-7560U) et j'ai obtenu des modifications intéressantes.
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 cette référence
QMap :: iterator utilise key () et value () - qui se trouvent facilement dans la documentation de Qt 4.8 ou la documentation de Qt-5 .
Modifier:
Une boucle for basée sur la 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 une paire.
La meilleure façon d'écrire ceci est sans boucle for basée sur la plage:
auto end = extensions.cend();
for (auto it = extensions.cbegin(); it != end; ++it)
{
std::cout << qPrintable(it.key()) << "," << qPrintable(it.value());
}
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 ceci fonctionnera:
for( auto key: extensions.keys() )
{
fout << key << "," << extensions.value( key ) << '\n';
}
Vous pouvez également utiliser des itérateurs à la place, consultez le lien hmuelners si vous préférez les utiliser.
J'ai utilisé quelque chose comme ça pour atteindre mon propre résultat. Juste au cas où quelqu'un aurait besoin des clés et des valeurs séparément.
{
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;
};
}
Une autre méthode pratique, à partir de QMap Docs . Il permet un accès explicite à la clé et à la valeur (itérateur de style Java):
QMap<QString, QString> extensions;
// ... fill extensions
QMapIterator<QString, QString> i(extensions);
while (i.hasNext()) {
i.next();
qDebug() << i.key() << ": " << i.value();
}
Si vous souhaitez pouvoir écraser, utilisez plutôt QMutableMapIterator
.
Il existe une autre méthode pratique Qt
, si vous ne souhaitez lire que les valeurs, sans les clés (en utilisant Qt
s foreach
et c ++ 11):
QMap<QString, QString> extensions;
// ... fill extensions
foreach (const auto& value, extensions)
{
// to stuff with value
}