-1

I get an iterator from a const QMap using QMap::find(), and through that iterator, I can change the content of the map. In my opinion this is a violation of the logical constness of the object. Am I wrong?

Is this allowed because compiler cannot recognize the what is been changed because of the Qt API?

void OpticalTrackingSystem::UpdateTrackability(const QUuid &uid) const
{
  // m_ObjectsToTrack is a member, therefore const
  ObjectMap::iterator it = m_ObjectsToTrack.find(uid);

  it.value().m_Trackable = true; // assignment to const!
  it.value().m_Moved = false;
}

In case it matters, my compiler is MSVC.

Edit: ObjectMap is defined as

struct ObjectProperties
{
    ObjectProperties(const ObjectToTrack &obj)
        : m_Obj(obj) { m_Trackable = false; m_Moved = false; }
    ObjectToTrack m_Obj;
    bool m_Trackable;
    bool m_Moved;
};
typedef QMap<QUuid, ObjectProperties> ObjectMap; 
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
  • The function you quoted is not const. The parameter of the function is a `const&` but the function itself is not. So `this` is a non const `OpticalTrackingSystem*` so `m_ObjectsToTrack` is non const. – JSF Sep 16 '15 at 13:54
  • Typo error from me. Thanks already edited. It is const. – code_not_yet_complete Sep 16 '15 at 13:58
  • Can you show the declarations of ObjectMap::find and OpticalTrackingSystem::m_ObjectsToTrack – JSF Sep 16 '15 at 14:04
  • @JSF there is an x at then end of your comment you can click on that will delete your comment. it will be after edit and you can only see it when you hover on the comment. – NathanOliver Sep 16 '15 at 14:17
  • Thanks for pointing out that. This seems to be the reason for apparently. Strange the interface is like that. – code_not_yet_complete Sep 16 '15 at 14:20
  • I'm confused over the relationship between the QMap you say you are using and the ObjectMap you seem to be using. In the documentation for QMap http://doc.qt.io/qt-4.8/qmap.html#ConstIterator-typedef it says the const copy of find returns a const_iterator, so the const must have been dropped before the call to find. – JSF Sep 16 '15 at 14:25
  • ObjectMap is just a typedef for QMap. However, find is casted to iterator from const_iterator – code_not_yet_complete Sep 16 '15 at 14:28
  • What do you mean "find is casted to iterator"? Where does that occur? The implicit cast in the assignment should not work. – JSF Sep 16 '15 at 14:31
  • ObjectMap::iterator it = m_ObjectsToTrack.find(uid); this should not be allowed as per the documentation you pointed out, isnt it? – code_not_yet_complete Sep 16 '15 at 14:31
  • That documentation includes neither complete documentation on the constructor of the iterator nor any documentation on the `operator=` so that assignment ought to be invalid (if the rhs is const_iterator) but the documentation did not make that totally clear. – JSF Sep 16 '15 at 14:36

2 Answers2

1

const augments data-hiding and encapsulation to provide full compile-time safety.

C++ supports only one level of constant. In your example, it means that your function cannot add, remove or replace elements from the map. Yet manipulating its members is fair game.

Zaid Amir
  • 4,727
  • 6
  • 52
  • 101
  • Ja, but stl map can not be manipulated the same way. So, it seems like compiler can not figure out from qmap but from stl map it is possible, – code_not_yet_complete Sep 16 '15 at 14:07
  • @code_not_yet_complete well I've never used QT before but I assume the way QMap is designed allows for such behavior. – Zaid Amir Sep 16 '15 at 14:09
  • But I am just wondering if this is only for MSVC and GCC is capable of handling it. ;) – code_not_yet_complete Sep 16 '15 at 14:12
  • @code_not_yet_complete You did not ask that in your question. As I said, I never used QT so maybe the GCC compiler is detecting a const violation that MSVC cannot detect. – Zaid Amir Sep 16 '15 at 14:19
  • 1
    @RedSerpent, while `**const` does not pass the const through to the lower level, most containers and iterators do. `find` on most const containers gives const_iterator and `value()` on typical const_iterator (that have such a function) gives `Tconst&`. So at one of those levels something unusual must be happening. – JSF Sep 16 '15 at 14:29
1

QMap has both const and non-const find methods:

      iterator find(const Key & key)
const_iterator find(const Key & key) const

just like std::map:

      iterator find (const key_type& k);
const_iterator find (const key_type& k) const;

The QMap iterators do differ in the constness of value():

      T& QMap::      iterator::value() const;
const T& QMap::const_iterator::value() const;

So you shouldn't be able to manipulate the values through the returned iterator. But you assign the returned iterator to a ObjectMap::iterator - I would expect an error at that point, but without a definition of ObjectMap and its iterator, I'm unable to test that.

Certainly the following MCVE demonstrates a compile-time error:

#include <QMap>

int main()
{
    const QMap<int,float> m{{1, 1.0}, {5, 5.0}};
    auto it = m.find(1);
    it.value() = 2.0;
    //         ^
    // error: assignment of read-only location
    //       ‘it.QMap<Key, T>::const_iterator::value<int, float>()’
}

Assigning the iterator explicitly gives the other expected error:

#include <QMap>

int main()
{
    const QMap<int,float> m{{1, 1.0}, {5, 5.0}};
    QMap<int,float>::iterator it = m.find(1);
    //                                     ^
    // error: conversion from ‘QMap<int, float>::const_iterator’
    //        to non-scalar type ‘QMap<int, float>::iterator’ requested
    it.value() = 2.0;
}

(I compiled with g++ -std=c++11 -Wall -Wextra -fPIC $(pkg-config --cflags Qt5Core) in both cases)

Edit

In the absence of a proper example in the question, I've tried to infer the missing declarations. But I still get the required error from gcc when I try to compile this:

#include <QMap>
#include <QUuid>

struct ObjectToTrack
{
};

struct ObjectProperties
{
    ObjectProperties(const ObjectToTrack &obj)
        : m_Obj(obj), m_Trackable(), m_Moved() {}
    ObjectToTrack m_Obj;
    bool m_Trackable;
    bool m_Moved;
};
typedef QMap<QUuid, ObjectProperties> ObjectMap;

struct OpticalTrackingSystem
{
    ObjectMap m_ObjectsToTrack;
    void UpdateTrackability(const QUuid &uid) const;
};

void OpticalTrackingSystem::UpdateTrackability(const QUuid &uid) const
{
  ObjectMap::iterator it = m_ObjectsToTrack.find(uid);
  //                                                ^
  // error: conversion from ‘QMap<QUuid, ObjectProperties>::const_iterator’ to
  //        non-scalar type ‘QMap<QUuid, ObjectProperties>::iterator’ requested

  it.value().m_Trackable = true;
  it.value().m_Moved = false;
}

My conclusion is that there's nothing wrong with the code in the question. Either that compiler has a bug or there's important information missing from the question that's different to my reconstruction above.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
  • struct ObjectProperties { ObjectProperties(const ObjectToTrack &obj):m_Obj(obj) { m_Trackable = false; m_Moved = false; } ObjectToTrack m_Obj; bool m_Trackable; bool m_Moved; }; typedef QMap ObjectMap; This is the map. The problem is it seems const_iterator is converted to iterator. I am just wondering this comes from MSVC. – code_not_yet_complete Sep 16 '15 at 14:48
  • That would be more useful (and more readable) in the question. I've edited it in for you this time. – Toby Speight Sep 16 '15 at 14:58
  • This definitely compiles. The problem is with classes. If my understanding is correct const find() function should be used in the const function. However, as I have shown this is casted to a iterator which means it either uses non const find or somethingelse is going on. – code_not_yet_complete Sep 16 '15 at 15:13
  • Which "this" compiles? Both my examples should _not_ compile. If your compiler doesn't diagnose the problem, then it's broken. If it does correctly identify those errors, then there's something different about your program that's at fault, such as `m_ObjectsToTrack` being declared `mutable` or something. Where you do your `find`, you should be able to create a reference such as `const ObjectMap& map = m_ObjectsToTrack;` but not `ObjectMap& map = m_ObjectsToTrack;`, which should produce an error. If it doesn't, then `m_ObjectsToTrack` isn't being seen as `const` and we can dig deeper. – Toby Speight Sep 16 '15 at 15:29
  • Sorry i meant compile with the error both your programs. But, inside const function the code in the question I posted does not give any errors which should not happen. And it is not a mutable variable. – code_not_yet_complete Sep 16 '15 at 15:31
  • I asked a while ago to see the declaration of `OpticalTrackingSystem::m_ObjectsToTrack`. You seem to be narrowing the question more and more toward the problem being there. – JSF Sep 16 '15 at 15:51
  • @JSF, I've added my inference of the missing declarations into my answer. Since that correctly fails to compile, we have only two possible conclusions: code_not_yet_complete either has a broken compiler, or is hiding something non-obvious in the actual declarations. – Toby Speight Sep 16 '15 at 16:54
  • @jsfNtoby so the same thing inside a const function compiles with error for you? I rechecked and if I use a stl map it gives me the correct error but with qmap it definitely compiles with no erorrs no warning. – code_not_yet_complete Sep 17 '15 at 06:48
  • The other possibility is that you're using an older Qt version than me (5.2) and there's a bug that's fixed in between. Or that Qt has some workaround for a compiler bug, and that gives this side-effect. – Toby Speight Sep 17 '15 at 06:59