Assume that I have a Widget class and a collection of Widgets:
class Widget
{
public:
void mutate() {}
};
class WidgetCollection
{
private:
std::vector<Widget> vec;
public:
Widget* findNiceWidget() { /*...*/ }
const Widget* findNiceWidget() const { /*...*/ }
};
WidgetCollection::findNiceWidget() gives access to one of the Widgets in the collection that fulfills some criteria.
Now, I have a class WidgetTags which allows to assign tags to Widgets. Tags are not part of Widgets, but are stored outside in this class:
class Tag {};
class WidgetTags
{
private:
std::multimap<const Widget*, Tag> tags;
public:
void addTag(const Widget* w, Tag t) { /*...*/ }
std::vector<const Widget*> getWidgetsWithTag(Tag t) const { /*...*/ }
};
The class only deals with pointers to const Widgets as it never changes any Widget, it just associates additional data with them.
Then I introduce three functions to work on these structures.
// Takes Widgets from the WidgetCollection and assigns tags to them in an instance of WidgetTags
void tagWidgets(const WidgetCollection& wc, WidgetTags& wt)
{
Tag niceTag;
wt.addTag(wc.findNiceWidget(), niceTag);
}
// Prints all Widgets which have the niceTag tag
void printNiceWidgets(const WidgetTags& wt)
{
Tag niceTag;
for (auto w : wt.getWidgetsWithTag(niceTag))
std::cout << w;
}
// Calls a mutating function on all Widgets which have the niceTag tag
void mutateNiceWidgets(const WidgetTags& wt)
{
Tag niceTag;
for (auto w : wt.getWidgetsWithTag(niceTag))
w->mutate();
}
The problem I am facing now is that mutateNiceWidgets() obviously cannot call mutate() as it only has const access to the Widgets returned by WidgetTags, even if I pass WidgetTags as a non-const reference.
To solve this, I would have to rewrite WidgetTags to store pointers to non-const Widgets and provide them with a non-const getWidgetsWithTag(). But if I do that, then WidgetTags::addTag() would have to accept pointers to non-const as well.
And if I do that, then I get problems in tagWidgets(), which would then have to accept a non-const reference to WidgetCollection, which in my opinion is incorrect, as it is not supposed to alter the collection (and in fact, the calling code might not even be able to provide a non-const reference).
I am confused as to how const correctness is supposed to be applied in this situation.
In my use case, all Widgets are managed and owned by the WidgetCollection. So theoretically, you should be granted non-const access to a Widget if you have non-const access to the whole WidgetCollection, no matter how you obtained the Widget.
Which makes me think about implementing something like this:
class WidgetCollection
{
// ...
Widget* makeNonConst(const Widget* w)
{
return const_cast<Widget*>(w);
}
};
And rewriting mutateNiceWidgets():
void mutateNiceWidgets(WidgetCollection& wc, const WidgetTags& wt)
{
Tag niceTag;
for (auto w : wt.getWidgetsWithTag(niceTag))
wc.makeNonConst(w)->mutate();
}
But this feels extremely wrong.
Any thoughts on how to make the right choice about what should be const and what shouldn't are appreciated.