0

Suppose we a class Mutable which has a non-const member function, void Mutable::mutate(), and a class having a data member of type Mutable. Calls to mutate on said member in a function that is declared const is then prohibited. In code:

class Mutable
{
public:
    void mutate();
};

class UseMutable
{
    Mutable m;
public:
    int compute() const
    {
        // does not compile
        m.mutate();
        return 42;
    }
};

If the type of m were a reference or a (shared) pointer to Mutable instead, we lose the compile time protection:

class UseMutablePtr
{
    Mutable* m;
public:
    int compute() const
    {
        // compiles =(
        m->mutate();
        return 42;
    }
};

or

class UseMutableRef
{
    Mutable& m;
public:
    int compute() const
    {
        // compiles =(
        m.mutate();
        return 42;
    }
};

How does one prevent this behaviour?

If only const member functions of Mutable are to be called, one can use const Mutable& or const Mutable* (or the corresponding std::..._ptr<const Mutable>, but what if one wants to call both const and non-const member functions of Mutable?

For pointers one could use a custom smart pointer which overloads the arrow and dereferencing operators as

template<class T>
class CustomSmartPtr
{
    T* ptr;
public:
    T* operator->() { return ptr; }
    const T* operator->() const { return ptr; }

    T& operator*() { return *ptr; }
    const T& operator*() const { return *ptr; }
};

since the smart pointers in the standard libary behave like normal pointers in this regard. For a replacement of unique_ptr this is not that big of a hassle, but for shared_ptr quite some effort is required.

For references the only thing that comes to mind is to reimplement std::reference_wrapper with the desired behaviour, i.e. to have conversion operators with the desired signature. This is again not too bad.

Are there "cheaper" ways to enforce the desired behaviour, or is there maybe some tooling that would at least detect violations?

Out of curiosity: why is this the default?

I have not yet come across an instance where this default behaviour is really desired, but have seen subtle bug that could have been prevented. Is this behaviour at least partially due to interoperability with C?

Dominik
  • 101
  • 3
  • 1
    One question per post. – Jason Oct 02 '22 at 18:28
  • `to enforce the desired behaviour` What is the desired behavior with `char*** pointer;`? – KamilCuk Oct 02 '22 at 18:32
  • @KamilCuk: I would want `char***` to act as `char const* const* const*`, i.e. as a read-only variable in const contexts. – Dominik Oct 02 '22 at 18:48
  • The basic rule/problem is that `const` applies to a single object, and whenever your have a pointer or reference, you have two objects -- the one containing the pointer or reference, and the one pointed at or referred to. Either of these objects being const has no effect on the const-ness of the other. – Chris Dodd Oct 02 '22 at 20:55
  • @ChrisDodd I am aware of that. I would like to make sure that one cannot call non-const member functions of the pointee in const contexts, if somehow possible without reinventing the weel (i.e. without rewriting the STL smart pointers with the appropriate operator overloads presented in the question). – Dominik Oct 02 '22 at 21:21

0 Answers0