3

So, I have simple code

QMap<QColor, int> colors;
for(int w = 0; w < image.width(); ++w)
    for (int h = 0; h < image.height(); ++h)
        colors[QColor::fromRgb(image.pixel(w,h))]++;

The error message is

no match for 'operator<' (operand types are 'const QColor' and 'const QColor').

So, qMapLessThanKey is trying unsuccessfully to instantiate comparer of two colors and it's impossible.

Question is: Is it possible to store QColor in a QMap as key as value and not by reference?

Just curious. I know how to write what I want in other way. But it looks for me strange that there is any exceptions in QT on what I can store in map or cannot.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
cassandrad
  • 3,412
  • 26
  • 50
  • It isn't an exception in Qt but a question of class design of QColor. A map cannot work without the operator<, as Jefffrey points out. It is just that std::map is "more flexible" in construction. – Gombat Sep 10 '15 at 22:26
  • @Gombat by why do you think that it's question of design of QColor, but not QMap? It's right that we can't compare colors. And it's obvious that often we need to have key-value collection sorted by value and not by key. I can't figure out the reason of such implementation. – cassandrad Sep 10 '15 at 22:28
  • The key-value collection has to be sorted by key to find the key value pair fastly. Without key comparison, a binary tree search would not work. – Gombat Sep 11 '15 at 07:29
  • Besides `std::map`, ` QHash` is also an option. This requires an overload for `qHash(QColor)` – MSalters Sep 11 '15 at 14:25
  • @cassandradied: A key-value collection **by definition** is sorted by key, not value. That is because a Key-value collection stores data **pairs**, and the half of the pair by which the collection is indexed is what's called the "key". The other part which we call "value" may hold any data and is not subject to constraints. – MSalters Sep 11 '15 at 14:30
  • @MSalters Every reasonable container places requirements on **all** types it's parametrized on - key types, value types, etc. E.g. `QMap` won't work with a non-default-constructible value type. Or try passing a random type as an allocator to C++ standard collections. – Kuba hasn't forgotten Monica Sep 11 '15 at 15:57

2 Answers2

5

No, because QColor doesn't provide operator<, which is required by QMap's Key type:

The key type of a QMap must provide operator<() specifying a total order.

An option would be to define operator< for QColor yourself, but I wouldn't advise it, as I'm not sure it's supposed to be defined.

I would recommend just to use std::map with a custom comparator (the third template argument) along the lines of:

struct color_compare {
    bool operator()(QColor const&, QColor const&) { /* ... */ }
};

std::map<QColor, Value, color_compare> map;
// ...
Shoe
  • 74,840
  • 36
  • 166
  • 272
  • Unfortunately, I'm using QMap and not std::map and there is no way to pass comparator to constructor. The question is more about — is it bad design of QMap or there is another suitable collection for storing key-value and have a fast way to update value. – cassandrad Sep 10 '15 at 22:06
  • @cassandradied It's not possible unless you define `operator<` for `QColor` yourself. Why can't you use `std::map` exactly? – Shoe Sep 10 '15 at 22:13
  • I can. It's just unexpected for me that there is some collection in QT that restricts type which it able to store. – cassandrad Sep 10 '15 at 22:26
  • "I'm not sure it's supposed to be defined." It won't hurt any to define it. It's a Qt bug, pretty much. – Kuba hasn't forgotten Monica Sep 11 '15 at 14:21
  • 1
    @cassandradied "unexpected for me that there is some collection in QT that restricts type which it able to store" That's normal, **all collections** (stdc++, Qt, boost, ...) have certain requirements to the types that are used with them. In particular, `QMap` requires that there exist a partial ordering on the key type, expressed by `bool operator<(const K&, const K&)` where `K` is the key type. Other containers have other requirements, .e.g. `QHash` doesn't care about ordering, but needs a hash function to exist. – Kuba hasn't forgotten Monica Sep 11 '15 at 15:53
1

Sure, it's possible. This is a missing Qt feature. You can implement the comparison operator yourself, comparing the R,G,B,A values lexicographically:

// https://github.com/KubaO/stackoverflown/tree/master/questions/qmap-qcolor-32512125
#include <QtGui>

bool operator<(const QColor & a, const QColor & b) {
   return a.redF() < b.redF()
       || a.greenF() < b.greenF()
       || a.blueF() < b.blueF()
       || a.alphaF() < b.alphaF();
}

int main() {
   Q_ASSERT(QColor(Qt::blue) < QColor(Qt::red));
   Q_ASSERT(QColor(Qt::green) < QColor(Qt::red));
   Q_ASSERT(QColor(Qt::blue) < QColor(Qt::green));
   Q_ASSERT(! (QColor(Qt::red) < QColor(Qt::red)));
   QMap<QColor, int> map;
   map.insert(Qt::red, 0);
   map.insert(Qt::green, 1);
   map.insert(Qt::blue, 2);
   Q_ASSERT(map.size() == 3);
   Q_ASSERT(map.cbegin().key() == Qt::red);
   Q_ASSERT((map.cbegin()+1).key() == Qt::green);
   Q_ASSERT((map.cbegin()+2).key() == Qt::blue);
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • 1
    It's logically not right to compare colors. Why Qt::blue is less than Qt::red? – cassandrad Sep 11 '15 at 15:36
  • @cassandradied Why not? All that `QMap` requires is a partial ordering of the key type. The operator we defined provides such partial ordering. And that's that. You can define **any partial ordering** on a given key type, whether it "makes sense" doesn't matter. It needs to be a [partial ordering](https://en.wikipedia.org/wiki/Partially_ordered_set) and that's that. – Kuba hasn't forgotten Monica Sep 11 '15 at 15:49