Search code examples
c++const-correctnesspimpl-idiom

C++ const correctness vulerability or unintended usage?


I am missing something or const-correctness doesn't work quite as intended with pointers (or perhaps smart pointers since that is what I've tested?). Anyway, here is what I observed with trying out a variant of the PIMPL idiom.

I have the following declared:

class A {
public:
  A(...);
  ...
  bool update_stuff(...) const;
  ...
protected:
  bool update_stuff_impl(...) const;
  ...
private:
  struct pimpl;
  pimpl* m_pimpl;
};

And for the implementation, I have something like:

struct A::pimpl {
  pimpl(...): some_data(new some_type());
  ...
  some_method1(...); // Modifies content of some_data
  some_method2(...); // Modifies content of some_data 
  ...

  boost::shared_ptr<some_type> some_data;
};

A::A(...): m_pimpl(new pimpl(...)) {
  ...
}

bool A::update_stuff(...) const {
   if( !update_stuff_impl(...) ) {
      return false;
   }
   return true;
}

bool A::update_stuff_impl(...) const {
   //-Change content of pimpl::some_data here
   m_pimpl->some_method1(...);
   m_pimpl->some_method2(...);
   return true;
}

What I'm having trouble understanding is how I could get away with using const qualifier for the functions A::update_stuff(...) and A::update_stuff_impl(...) when I am actually modifying A::pimpl::some_data??! Or is this expected behavior or just plain bad usage? If it is one of the latter, appreciate if you can identify how it can be corrected?

Thanks for your time and interest.


Solution

  • It's not a new discovery, you can read up on something like 'const is shallow' in C++. What leads to natural distinction between physical and logical const (read after the second too).

    If you have pointer in class, be it smart or dumb, you're likely involved in this problem and must carefully design. Taking into account that modifying the attached data on the other end of pointer is not discovered.

    Possible workararounds are to make the pointer const T* and add private member function that returns T*. Another is to restrict direct access to the pointer in general, and require a pair of functions one const and other nonconst.