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?