我有一个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
类型吗?
如果您想要first
和second
的STL样式,请这样做:
for(auto e : extensions.toStdMap())
{
fout << e.first << "," << e.second << 'n';
}
如果您想使用Qt提供的功能,请这样做:
for(auto e : extensions.keys())
{
fout << e << "," << extensions.value(e) << 'n';
}
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 ↦
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';
}
这里还有一个包装器
对于对优化感兴趣的人,我尝试了几种方法,做了一些微基准测试,我可以得出结论,STL风格的方法明显更快。
我试过用这些方法添加整数:
- QMap:值()
- 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样式的性能非常相似
QMap::iterator使用key()和value() -可以在Qt 4.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());
}
在"旧的" 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链接
自Qt 5.10以来,您可以使用一个简单的包装器类来使用基于范围的for循环,但仍然能够访问map条目的键和值。
将以下代码放在源文件的顶部或包含的头文件中:
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';
}
e
的类型将是std::pair<const QString&, const QString&>
,正如QKeyValueIterator文档中部分指定的那样。
成员变量map
是映射的隐式共享副本,以避免在与临时值一起使用时出现分段错误。因此,只要不在循环中修改映射,这只会带来很小的常量开销。
上面的例子使用了c++ 17中引入的类模板实参演绎。如果使用较旧的标准,则必须在调用构造函数时指定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';
}
KDAB的Ivan Čukić有一篇博客文章,解释了如何使用c++ 17结构化绑定迭代QMap
而不复制容器:
template <typename T>
class asKeyValueRange
{
public:
asKeyValueRange(T& data) : m_data{data} {}
auto begin() { return m_data.keyValueBegin(); }
auto end() { return m_data.keyValueEnd(); }
private:
T& m_data;
};
...
QMap<QString, QString> extensions;
for (auto&& [key, value]: asKeyValueRange(extensions))
{
fout << key << ',' << value << 'n';
}
另一个方便的方法,来自QMap文档。它允许显式访问键和值(java风格的迭代器):
QMap<QString, QString> extensions;
// ... fill extensions
QMapIterator<QString, QString> i(extensions);
while (i.hasNext()) {
i.next();
qDebug() << i.key() << ": " << i.value();
}
如果您希望能够覆盖,请使用QMutableMapIterator
。
如果您只对读取值感兴趣,而不需要键(使用Qt
、foreach
和c++11),还有另一种方便的Qt
方法:
QMap<QString, QString> extensions;
// ... fill extensions
foreach (const auto& value, extensions)
{
// to stuff with value
}
我使用了这样的东西来实现我自己的结果。以防万一有人需要把键和值分开。
{
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;
};
}
自Qt 6.4以来,您可以使用asKeyValueRange()
方法如下:
for(auto pair : extensions.asKeyValueRange()) {
pair.first; // key access
pair.second; // value access
}
这甚至适用于花哨的结构化绑定。
for(auto& [key, value] : extensions.asKeyValueRange()) {
fout << key << "," << value << 'n';
}