60

オブジェクトがあり、QMapそのコンテンツをファイルに書き込もうとしています。

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

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

なぜ私は得るのですか:error: 'class QString' has no member named 'first' nor 'second'

eタイプではありませんかQPair?

4

9 に答える 9

63

firstとを使用して STL スタイルが必要な場合は、次のsecondようにします。

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

Qt が提供するものを使用したい場合は、次のようにします。

for(auto e : extensions.keys())
{
  fout << e << "," << extensions.value(e) << '\n';
}
于 2011-12-16T02:18:33.637 に答える
55

C++11 の range-based-for は、逆参照された反復子の型を自動的に推定される「カーソル」型として使用します。ここでは、式の型です*map.begin()
また、QMap::iterator::operator*()( 型の) 値への参照を返すため、QString &そのメソッドを使用してキーにアクセスすることはできません。

ドキュメントに記載されているイテレータ メソッドのいずれかを使用する必要がありますが、使用は避ける必要があります。

  • keys()キーのリストを作成し、各キーの値を検索する必要があるため、または、
  • toStdMap()すべてのマップ要素を別のマップ要素にコピーするため、

それはあまり最適ではありません。


ラッパーを使用して型QMap::iteratorとして取得することもできます。auto

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

ここに別のラッパーがあります。

于 2011-12-16T02:51:00.050 に答える
28

最適化に興味がある人のために、私はいくつかのアプローチを試し、いくつかのマイクロ ベンチマークを行いました。STL スタイルのアプローチの方がはるかに高速であると結論付けることができます。

これらのメソッドで整数を追加しようとしました:

  • QMap::values()
  • Java スタイルの反復子 (ドキュメントでアドバイスされているとおり)
  • STLスタイルのイテレータ(ドキュメントでもアドバイスされているように)

そして、それを QList/QVector の整数の合計と比較しました

結果 :

Reference vector :   244  ms
Reference list :     1239  ms

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

興味のある方のためのコード:

#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";
}

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 スタイルと Java スタイルのパフォーマンスは非常に似ています。

于 2015-07-05T01:34:29.693 に答える
21

QMap :: iteratorはkey()とvalue()を使用します。これらはQt4.8のドキュメントまたはQt-5のドキュメントで簡単に見つけることができます。

編集:

範囲ベースの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 *()はQMap :: iterator :: value()と同等であり、ペアを提供しません。

これを書く最良の方法は、範囲ベースのforループを使用しないことです。

auto end = extensions.cend();
for (auto it = extensions.cbegin(); it != end; ++it)
{
    std::cout << qPrintable(it.key()) << "," << qPrintable(it.value());
}
于 2011-12-15T09:57:45.740 に答える
13

「古い」C++ では、Qt を使用すると、次のようになります。

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

私はここに C++11 コンパイラを持っていませんが、おそらく次のように動作します:

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

代わりにイテレータを使用することもできます。使用したい場合は hmuelners リンクをチェックしてください

于 2011-12-15T11:55:06.760 に答える
2

Qt 5.10 以降、単純なラッパー クラスを使用して範囲ベースの for ループを使用できますが、マップ エントリのキーと値の両方にアクセスできます。

次のコードを、ソース ファイルの先頭またはインクルードするヘッダーのどこかに配置します。

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();   }
};

すべてのエントリを反復処理するには、次のように単純に記述できます。

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

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

の型はeQKeyValueIterator のドキュメントstd::pair<const QString&, const QString&>で部分的に指定されているとおりになります。

メンバー変数mapは、マップの暗黙的な共有コピーであり、これが一時的な値で使用された場合のセグメンテーション違反を回避します。したがって、ループ内でマップを変更しない限り、一定のオーバーヘッドはわずかです。


上記の例では、C++17 で導入されたクラス テンプレート引数 deductionを使用しています。古い標準を使用している場合は、コンストラクターを呼び出すときに QMapWrapper のテンプレート パラメーターを指定する必要があります。この場合、ファクトリ メソッドが役立つ場合があります。

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

これは次のように使用されます。

for(auto e : wrapQMap(extensions))
{
  fout << e.first << "," << e.second << '\n';
}
于 2020-02-20T23:18:35.787 に答える
1

私は自分の結果を達成するために、このようなものを使用しました。誰かがキーと値を別々に必要とする場合に備えて。

{
   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;
   }; 
}  
于 2016-08-25T22:13:58.790 に答える