rolevax
rolevax

Reputation: 1690

Temporarily modifying fields in a const member function

Suppose that we have a class A with a member function f. To the outside world, f simply computes a value without modifying anything of A; but in the implementation, it does temporarily modify A:

class A
{
    int f() const
    {
        tiny_change(b); // since copying "b" is expensive
        int result = compute(b);
        tiny_recover(b); // "b" backs to the original value
        return result;
    }

    B b;
}

Of course the code above does not compile. Here are two workarounds I know:

  1. const_cast<A*>(this)->b
  2. mutable B b;

None of these solutions is perfect. The solution 1 involves UB when an instance of A itself is const; and the solution 2 exposes the mutable-ness to the whole class such that it cannot prevent the coder from accidentally modifying b in other const member functions.

const_cast is "local", but may trigger UB; mutable is memory-safe, but too "global".

So is there a third solution, or am I understanding something wrong?

Upvotes: 2

Views: 170

Answers (1)

celtschk
celtschk

Reputation: 19721

One possibility would be to encapsulate B in a class that has it mutable, but when it is const normally only allows const access, except that it befriends A::f. For example like this (untested code):

class A
{
  int f() const;
  int g() const; // some function without exclusive access

  class B_wrapper
  {
    friend int A::f() const;
  public:
    B& get() { return object; }
    B const& get() const { return object; }
  private:
    B& get_mutable() const { return object; }
    mutable B object;
  };
  B_wrapper bw;
};

int A::f() const
{
  B& b = bw.get_mutable(); // allowed due to friend declaration
  tiny_change(b); // since copying "b" is expensive
  int result = compute(b);
  tiny_recover(b); // "b" backs to the original value
  return result;
}

int A::g() const
{
  // B& b = bw.get_mutable();
  //   -> not allowed because B_wrapper::get_mutable() is private
  // B& b = bw.get();
  //   -> not allowed because get() const returns a const reference
  B const& b = bw.get();
  // without casts, only const interface to b is available
}

Upvotes: 2

Related Questions