Assuming you accept const-correctness as a technique, then I think that means you prefer compiler-checked const-correctness to brevity. So you want the compiler to check two things:
- That when "this" is non-const, the referand of the pointer you return can safely be modified by the caller.
- That when "this" is const, whatever work you do does not perform any non-const operations on "this".
If the const version calls the non-const then you don't get (2). If the non-const version calls the const one and const_casts the result, then you don't get (1). For instance suppose Bar
is actually char
, and the code you write ends up returning (in some cases) a string literal. This will compile (and -Wwrite-strings gives no warning), but your caller ends up with a non-const pointer to a string literal. This contradicts "you prefer compiler-checked const-correctness".
If they both call a helper member function Bar *getBar() const
, then you get both (1) and (2). But if it's possible to write that helper function, then why are you messing about with const and non-const versions in the first place, when it's perfectly OK to modify the Bar returned from a const Foo? Occasionally perhaps some detail of implementation means you're implementing an interface with two accessors even though you only need the one. Otherwise either the helper can't be written, or else the two functions can be replaced just by the single helper.
As long as code size is not a concern, I think the best way to achieve both (1) and (2) is to have the compiler actually consider both cases:
struct Bar { int a; };
struct Foo {
Bar *bar() { return getBar<Bar>(this); }
const Bar *bar() const { return getBar<const Bar>(this); }
Bar *bar2() const { return getBar<Bar>(this); } // doesn't compile. Good.
Bar *bar3() const { return getBar<const Bar>(this); } // likewise
private:
template <typename B, typename F>
static B *getBar(F *self) {
// non-trivial code, which can safely call other functions with
// const/non-const overloads, and we don't have to manually figure out
// whether it's safe to const_cast the result.
return &self->myBar;
}
Bar myBar;
};
If the code is trivial, like an operator[]
which accesses some array owned by the object, then I would just duplicate the code. At some point, the above function template is less coding effort than the duplication, at which point use the template.
I think the const_cast approach, while clever and seemingly standard, just isn't helpful, because it chooses brevity over compiler-checked const-correctness. If the code in the method is trivial, then you can duplicate it. If it's not trivial, then it's not easy for you or a code maintainer to see that the const_cast is actually valid.