Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I allowed to call a non-const member function from a const_iterator if the container element is a pointer?

Consider the following code:

#include <vector>
using namespace std;

struct foo
{
  void bar()
  {
  }
};

int main()
{
  {
    vector<foo*> a;
    a.push_back(new foo());
    a.push_back(new foo());
    a.push_back(new foo());

    vector<foo*>::const_iterator itr = a.begin();
    (*itr)->bar(); // compiles - this becomes more confusing 
                   // when found in a const method. On first 
                   // glance, one will (or at least me) may
                   // assume that bar() must be const if the 
                   // method where it is being called from is 
                   // const

    // The above compiles because internally, this is what happens 
    // (ignore the fact that the pointer has not been newd)
    foo* const * element;
    (*element)->bar(); // compiles

    // What I would expect however (maybe it is just me) is for const_iterator
    // to  behave something like this
    const foo* const_element;
    const_element->bar(); // compile error
  }

  {
    vector<foo> a;
    a.resize(10);

    vector<foo>::const_iterator itr = a.begin();
    itr->bar(); // compile error
  }

}

I understand why it can be called. The const_iterator stores the const-ness like this: const T* which for the pointer translates to foo* const * and for the object foo const *.

So my question is, why are we allowed to call a non-const member function from a const_iterator? Isn't it more intuitive to not allow the call to a non-const member function from a const_iterator? Shouldn't the design of iterators with the const option prevent this behaviour?

The more important question now is this: What if I want const_iterator to disallow calling of non-const member functions of the pointed to object?

like image 311
Samaursa Avatar asked Feb 21 '23 05:02

Samaursa


2 Answers

Shouldn't the design of iterators with the const option prevent this behaviour?

It does. You're just expecting it to be a different operation.

As you discovered, a container of pointers... contains pointers, not objects. Therefore, a const_iterator to such pointers means that the pointers are constant, not the objects they point to.

That's not going to change, nor should it. The standard library containers are generally designed to contain full-fledged objects, not pointers. So they shouldn't encourage users to make vectors of pointers and other dubious constructs.

If you really need a vector to contain pointers, then you should use a container that is actually designed to do so. Like Boost's pointer container classes. Their const_iterators make the objects being pointed to const properly. They also do other useful things, like own the objects they point to (so that they are properly deleted) and so forth.

like image 137
Nicol Bolas Avatar answered Feb 22 '23 19:02

Nicol Bolas


You have a vector of pointers, pointers don't have member functions so you're not calling a member function on something that is stored in the vector.

The type of object that you get when you dereference a pointer depends on the type of that pointer. Your vector is a vector of non-const pointers so when you derefence any pointer from your container you always get a non-const reference to the pointed to object.

If you want a vector of pointers then you have two options. You can create a vector<const foo*> instead and you will never be able to retrieve a non-const reference to any pointer to object or, if you need to be able to get non-const references from non-const instances of your vector you will have to create an object that contains the vector as a private member variable and gives you the access that you want via a pass-through interface.

If your vector is supposed to own the objects that it holds pointers to you could consider simple vector<foo> instead or, if dynamic allocation is required, a boost::ptr_vector<foo>.

like image 26
CB Bailey Avatar answered Feb 22 '23 18:02

CB Bailey