07.10.2022 12:47 Juha Nieminen kirjutas:
> I have encountered this small dilemma several times. It's a question of
> const correctness and proper OO design in C++.
>
> Should a virtual function in a base class be const or not? This especially
> in cases where it's very possible that a derived implementation wants
> to change its own state (and thus would want the function to not be
> const).
>
IMO, the 'const' should be applied logically. There is a virtual
function which does something. Is this something that conceptually
should not modify the object state? If yes, then make the virtual
function const.
If a derived class overrides this function, it should conceptually still
do something which does not modify the object. If it conceptually does,
then it's possible you have bigger problems with the design, starting
with the function name.
If the derived class modifies its state only technically, e.g. by
caching something, we have means to express this in C++ cleanly - that's
the 'mutable' keyword.
When following these rules, now if you have a const reference to the
object and need to call a non-const virtual function on it, this means
you want to call a function on a const object which might (again,
conceptually) modify the object state. That's a design smell, and this
is the exact reason why the language has the const qualifier in the
first place - to catch such things at compile time so the design could
be fixed.
In reality the things won't work out so nicely. In some cases the base
class virtual interface is at so abstract level it is impossible to say
if the functions might conceptually modify the object or not. According
to the above guidelines these virtual functions should be non-const,
which in turn means that this class cannot be really used via const
references. In my code I have got several "non-const" classes which are
basically always accessed only over non-const references. Yes, this
looses the benefits of 'const', but at least it is honest about that and
does not hide it behind a wall of const_cast-s.
Other scenario is e.g. with virtual functions which take callbacks or
functors as arguments. Some callbacks might want to modify the object,
some not. In that case one should provide two different virtual
functions, one const and the other non-const.
If there is some override which does not make sense and which should not
be called, then it's easy for me. I just put a debug assert failure and
a runtime exception there.
hth
Paavo