I need a user defined set according to the order I want. But When I wanted to access set members I got error The object has type qualifiers that are nor compatible with member function (I get this error when I place mouse pointer on error line. The error mentioned in title is fromm Error List after build)
typedef struct tagRECT
{
long left;
long top;
long right;
long bottom;
} RECT;
struct LabelRect : public RECT
{
bool isIsolatedFrom(LabelRect* pRect)
{
if (pRect->right < left ||
pRect->left > right ||
pRect->top > bottom ||
pRect->bottom < top)
return true;
return false;
}
};
class CDrawnLabel
{ public:
LabelRect m_LabelRect;
LabelRect* getLabelRect(){ return &m_LabelRect; }
bool operator<(CDrawnLabel & rhs)
{
//This is the set ordering
return getLabelRect()->right < rhs.getLabelRect()->right;
}
}
I have a set like following
typedef std::set<CDrawnLabel> DrawnLabelSet;
DrawnLabelSet m_setDrawnLabel
I got error when I tried to access set members
DrawnLabelSet::iterator itbegin,itend;
LabelRect* pRectSecond;
itbegin=m_setDrawnLabel.begin();
itend=m_setDrawnLabel.end();
pRectSecond=(*itbegin).getLabelRect();// Here I get the error.
The reason you get this error is because keys inside std::set<T> are stored as const T.
So this expression (*itbegin) returns a const CDrawnLabel. Only const member functions can be called from a const object.
You will have to make getLableRect const. Also since const member functions can only return const pointers/references the member should be:
const LabelRect* getLabelRect() const { return &m_LabelRect; }
Not required but it would be a good idea to make your comparator const as well since it's not modifying any data. Another improvement that can be done is instead of taking a reference you should pass a const ref to the comparator.
bool operator<(const CDrawnLabel &rhs) const
{
//This is the set ordering
return getLabelRect()->right < rhs.getLabelRect()->right;
}
The problem here is that std::set<>::iterator is actually a const_iterator so (*itbegin) has the type const CDrawnLabel&. Why is this? Well, if you could change the reference in the set, you could invalidate the ordering. So you need to take the object out of the set, modify it and then put it back in. Or, if you don't want to change it you could define a const function getConstLabelRect()
Related
Update
I have created an qt bugticket hoping the documentation will be extended.
Original Question
Believing an Question from 2010 and the Qt Documentation, the operator==() doesn't work with custom types.
Quote:
bool QVariant::operator==(const QVariant & v) const
Compares this QVariant with v and returns true if they are equal; otherwise returns false.
QVariant uses the equality operator of the type() it contains to check for equality. QVariant will try to convert() v if its type is not the same as this variant's type. See canConvert() for a list of possible conversions.
Warning: This function doesn't support custom types registered with qRegisterMetaType().
I've tried to reproduce the repro case from the Stackoverflow Question from 2010 and the comparison worked without any problems for me.
I also went a step further and tried comparisons using an own class which also worked perfectly.
To reproduce, put the following code into any header:
enum MyEnum { Foo, Bar };
Q_DECLARE_METATYPE(MyEnum)
class MyClass
{
int value;
public:
MyClass() : value(0)
{
}
MyClass(int a) : value(a)
{
}
bool operator==(const MyClass &) const
{
Q_ASSERT(false); // This method seems not to be called
return false;
}
bool operator!=(const MyClass &) const
{
Q_ASSERT(false); // This method seems not to be called
return true;
}
};
Q_DECLARE_METATYPE(MyClass)
And the following code into any function:
QVariant var1 = QVariant::fromValue<MyEnum>(Foo);
QVariant var2 = QVariant::fromValue<MyEnum>(Foo);
Q_ASSERT(var1 == var2); // Succeeds!
var1 = QVariant::fromValue<MyEnum>(Foo);
var2 = QVariant::fromValue<MyEnum>(Bar);
Q_ASSERT(var1 != var2); // Succeeds!
QVariant obj1 = QVariant::fromValue<MyClass>(MyClass(42));
QVariant obj2 = QVariant::fromValue<MyClass>(MyClass(42));
Q_ASSERT(obj1 == obj2); // Succeeds!
obj1 = QVariant::fromValue<MyClass>(MyClass(42));
obj2 = QVariant::fromValue<MyClass>(MyClass(23));
Q_ASSERT(obj1 != obj2); // Succeeds!
I would guess that in newer qt versions the size of a type is aquired when the Q_DECLARE_METATYPE is used so the QVariant can compare values of unknown types bytewise.
But that's only a guess and I don't want to risk the stability of my application by guessing what qt does instead of relying on the documentation.
Can I find out, how the QVariant compares unknown types? I would prefer relying on specification than on implementation.
I'm afraid you'll need to rely on the code (and, being behaviour, it can't be changed without breaking), and not documentation. There's a surprise just below, though.
Here's the relevant code.
QVariant::operator== for types with unregistered operators will just use memcmp. The relevant snippet (in 5.1) is this:
bool QVariant::cmp(const QVariant &v) const
{
QVariant v1 = *this;
QVariant v2 = v;
if (d.type != v2.d.type)
// handle conversions....
return handlerManager[v1.d.type]->compare(&v1.d, &v2.d);
}
handlerManager is a global object that gets used to perform type-aware manipulations. It contains an array of QVariant::Handler objects; each of such objects contains pointers to perform certain operations on the types they know how to handle:
struct Handler {
f_construct construct;
f_clear clear;
f_null isNull;
f_load load;
f_save save;
f_compare compare;
f_convert convert;
f_canConvert canConvert;
f_debugStream debugStream;
};
Each and every of those members is actually a pointer to a function.
The reason for having this array of global objects is a bit complicated -- it's for allowing other Qt libraries (say, QtGui) to install custom handlers for the types defined in those libs (f.i. QColor).
The operator[] on the handlerManager will perform some extra magic, namely get the right per-module handler given the type:
return Handlers[QModulesPrivate::moduleForType(typeId)];
Now the type is of course a custom type, so the Handler returned here is the one the Unknown module. That Handler will use the customCompare function in qvariant.cpp, which does this:
static bool customCompare(const QVariant::Private *a, const QVariant::Private *b)
{
const char *const typeName = QMetaType::typeName(a->type);
if (Q_UNLIKELY(!typeName) && Q_LIKELY(!QMetaType::isRegistered(a->type)))
qFatal("QVariant::compare: type %d unknown to QVariant.", a->type);
const void *a_ptr = a->is_shared ? a->data.shared->ptr : &(a->data.ptr);
const void *b_ptr = b->is_shared ? b->data.shared->ptr : &(b->data.ptr);
uint typeNameLen = qstrlen(typeName);
if (typeNameLen > 0 && typeName[typeNameLen - 1] == '*')
return *static_cast<void *const *>(a_ptr) == *static_cast<void *const *>(b_ptr);
if (a->is_null && b->is_null)
return true;
return !memcmp(a_ptr, b_ptr, QMetaType::sizeOf(a->type));
}
Which, apart from a bit of error checking and handling shared and null variants in a special way, uses memcmp on the contents.
... only if the type is not a pointer type, it seems. Wonder why there's that code there...
Good news!
Starting with Qt 5.2, you can use QMetaType::registerComparator (see here) to make Qt invoke operator< and operator== on your custom type. Just add to your main:
qRegisterMetaType<MyClass>();
QMetaType::registerComparators<MyClass>();
And voilà, you'll hit the assert in your equality operator. QVariant::cmp now is:
QVariant v1 = *this;
QVariant v2 = v;
if (d.type != v2.d.type)
// handle conversions, like before
// *NEW IMPORTANT CODE*
if (v1.d.type >= QMetaType::User) {
// non-builtin types (MyClass, MyEnum...)
int result;
// will invoke the comparator for v1's type, if ever registered
if (QMetaType::compare(QT_PREPEND_NAMESPACE(constData(v1.d)), QT_PREPEND_NAMESPACE(constData(v2.d)), v1.d.type, &result))
return result == 0;
}
// as before
return handlerManager[v1.d.type]->compare(&v1.d, &v2.d);
I have a class roughly defined like below. Among others, it has a < comparison operator.
class DictionarySearchItem {
public:
DictionarySearchItem();
double relevance() const;
bool operator<(const DictionarySearchItem& item) { return relevance() > item.relevance(); }
};
typedef std::vector<DictionarySearchItem> DictionarySearchItemVector;
I then use the class this way:
DictionarySearchItemVector searchItems;
for (unsigned i = 0; i < entries.size(); i++) {
// ...
// ...
DictionarySearchItem item;
searchItems.push_back(item);
}
However when I try to sort the vector:
std::sort(searchItems.begin(), searchItems.end());
I'm getting the following compilation error with MinGW.
/usr/include/c++/4.2.1/bits/stl_algo.h:91: erreur : passing 'const hanzi::DictionarySearchItem' as 'this' argument of 'bool hanzi::DictionarySearchItem::operator<(const hanzi::DictionarySearchItem&)' discards qualifiers
I don't quite understand what is incorrect with my code and the error message is not clear to me. The same code compiles fine with MSVC2008. Any idea what could be the issue?
You need to make the less-than operator const:
bool operator<(const DictionarySearchItem& item) const { ... }
^
The reason is probably that sort relies on the fact that elements being compared cannot change as a result of the comparison. This can be enforced by having both sides of the < comparison be const, which means the operator has to be const, as well as it's argument.
I'm making a program that generates a maze and then uses bredth first search to find a way in the maze. My function that checks if an element is present in a container-class now uses the vector like this (where coordinatePath is a typedef for vector) :
bool Labyrinth::inVisited(const Coordinate &c, const coordinatePath &visited ) const
{
for each (Coordinate coord in visited)
{
if(coord == c)
return true;
}
return false;
}
Since this method has to traverse the full container if an element is not present it's very ineffective for large searches. I tried to implement the same function that uses a set instead of a vector and wrote it like this:
bool Labyrinth::inVisited(const Coordinate &c, const set<Coordinate> &visited ) const
{
return (visited.find(c) != visited.end());
}
when i try to recomplie i get a lot of errors where the topmost is
Error 22 error C2676: binary '<' : 'const Coordinate' does not define this operator or a conversion to a type acceptable to the predefined operator c:\program files (x86)\microsoft visual studio 11.0\vc\include\xstddef 193
I don't really understand these particular debug-messages and wonder if there is a way to implement this faster search!
In order to create a std::set of objects, those objects have to define operator <.
So you need to add the following operator:
inline bool operator < (const Coordinate& first, const Coordinate& other);
To use elements in a set the value_type has to define operator< or you need to provide a comparison functor to the container. Apparently, your Coordinate type doesn't do that or the operator< you provide takes incompatible arguments. It should look roughly like this:
struct Coordinate {
bool operator<(const Coordinate& other) const { return false; }
};
// or by providing a functor
struct CmpCoord {
bool operator()(const Coordinate& x, const Coordinate& y);
};
typedef std::set<Coordinate, CmpCoord> coord_set;
I'm getting this weird error:
error C2663:
'sf::Drawable::SetPosition' : 2
overloads have no legal conversion for
'this' pointer
I think it has something to do with const mismatches but I don't know where, or why.
In the following code I have a vector of shapes and sprites, and when trying to access one of the vectors shapes and calling one of its functions I'm getting the error.
std::vector<sf::Shape> Shapes;
std::vector<sf::Sprite> Sprites;
bool AddShape(sf::Shape& S){
Shapes.push_back(S); return true;
};
bool AddSprite(sf::Sprite& S){
Sprites.push_back(S); return true;
};
private:
virtual void Render(sf::RenderTarget& target) const {
for(unsigned short I; I<Shapes.size(); I++){
Shapes[I].SetPosition(
Shapes[I].GetPosition().x + GetPosition().x,
Shapes[I].GetPosition().y + GetPosition().y);
target.Draw(Shapes[I]);
}
for(unsigned short I; I<Sprites.size(); I++){
target.Draw(Sprites[I]);
}
}
How can I fix this?
Render is declared with a const after the parameters. This means it does not change its object. Which means, that all of the object's member variables are considered constants within Render, as changing their state means changing the containing object. Assuming Shapes is a member variable, and that SetPosition does change the shape (i.e. not declared as const), you cannot call it within a const member function.
So, remove the const from Render and you'll be fine (you fix your logic, in case it must be const).
I'm learning C++ and can't get my head around this problem:
I have a simple class A
class A {
private:
int ival;
float fval;
public:
A(int i = 0, float f = 0.0) : ival(i), fval(f) { }
~A(){ }
void show() const {
cout << ival << " : " << fval << "\n";
}
void setVal(int i) {
ival = i;
}
//const getters for both ival and fval
//used for the default "lesser"
friend bool operator<(const A& val1, const A& val2) {
return val1.ival < val2.ival ? true : false;;
}
}
Then I have a regular set<A> myset that gets filled with insert(A(2, 2.2)); in a loop.
Iterating to get all the values is not a problem but I want to modify the value within this iteration:
for(set<A>::iterator iter = set3.begin(); iter != set3.end(); iter++) {
iter->setVal(1);
}
I assume that this should be doable, like you would do it in Java within a foreach loop. When compiling I get error: passing ‘const A’ as ‘this’ argument of ‘void A::setVal(int)’ discards qualifiers.
Looking at the sources of the STL set, i see that begin() is only available as a const method and I think this might be the problem. Messing around with const on the setVal() method got always the same error and wouldn't make much sense since I want to modify the value of A.
Is this the wrong approach of changing a bunch of A's values with a loop?
The STL set does not let you change values it stores. It does that by returning a copy of the object through the iterator (not the actual one in the set).
The reason that set does this is because it's using < to order the set and it doesn't want to remake the entire tree every time you dereference the iterator, which it would have to do, since it doesn't know if you changed anything that changes the ordering.
If you need to update the set<>, remove the old value and add in a new one.
EDIT: just checked source to SGI STL and it says this:
typedef typename _Rep_type::const_iterator iterator;
So, a set::iterator is just a set::const_iterator
From this page, it seems that begin() exists as well as a non-const method.
Perhaps your set is passed into the method as a const reference ?
EDIT
The referenced page is wrong. As Scharron states, there is no non-const begin() (or end() for that matter) method for ordered containers.
I will inform the website about their mistake (it's not the first they made ;))