Search code examples
c++boostconstantsshared-ptr

How to make const apply to C++ class members that are shared pointers


I'm struggling a bit trying to work out how to pass const pointers to classes around using shared pointers. Normally you just declare a pointer to an object const and you can no longer change any members in it, however if the class is using shared pointers the const does not seem to apply.

Here is a compilable example of the problem. In processA(), it should not be possible to add another element to the vector as the object is supposed to be const.

What am I doing wrong?

#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>

struct C {
    int value;
};

struct B {
    std::vector<C> vectorOfC;
};

struct A {
    boost::shared_ptr<B> b;
};

typedef boost::shared_ptr<A> PtrA;

void processA(boost::shared_ptr<const A> a)
{
    // This should not be possible because 'a' points to const
    C c2;
    c2.value = 456;
    a->b->vectorOfC.push_back(c2);

    for (std::vector<C>::const_iterator
            i = a->b->vectorOfC.begin(); i != a->b->vectorOfC.end(); i++
    ) {
            std::cout << i->value << std::endl;
    }
}

int main(void)
{
    C c;
    c.value = 123;

    PtrA a(new A);
    a->b.reset(new B);
    a->b->vectorOfC.push_back(c);

    processA(a);
    return 0;
}

Solution

  • I think you're misunderstanding how const works with pointers.

    const T * doesn't declare a const pointer, it declares a pointer to const. I.e. the pointer is mutable, but the thing(s) it points at are not.

    When you add const to a pointer, you actually get T * const, which is a const pointer to mutable object(s), i.e. you can still change the pointee through the pointer.

    In the case of std::shared_ptr (or boost::shared_ptr) adding const leaves you with const std::shared_ptr<T>, i.e. you cannot change the shared_ptr, but you can change the pointee, because the shared_ptr does not point to an immutable object.

    To rectify this, you want to use std::shared_ptr<const T>, which is a mutable shared_ptr to a const object.

    You can get around this by:

    1. Making your boost::shared_ptrs pointers to const, or, if in certain instances it's acceptable to mutate the pointee
    2. Using overloads of a getter (one overload being const, the other being mutable) to properly return boost::shared_ptr<const T> in the proper case, or, if callers don't actually need the boost::shared_ptr itself
    3. Using overloads of a getter (one overload being const, the other being mutable) to return references to T, with the const overload returning const T & and the mutable overload returning T &