While striving for const-correctness, I often find myself writing code such as this
class Bar;
class Foo {
public:
const Bar* bar() const { /* code that gets
Another way could be to write a template that calls the function (using CRTP) and inherit from it.
template<typename D>
struct const_forward {
protected:
// forbid deletion through a base-class ptr
~const_forward() { }
template<typename R, R const*(D::*pf)()const>
R *use_const() {
return const_cast<R *>( (static_cast<D const*>(this)->*pf)() );
}
template<typename R, R const&(D::*pf)()const>
R &use_const() {
return const_cast<R &>( (static_cast<D const*>(this)->*pf)() );
}
};
class Bar;
class Foo : public const_forward<Foo> {
public:
const Bar* bar() const { /* code that gets a Bar somewhere */ }
Bar* bar() { return use_const<Bar, &Foo::bar>(); }
};
Note that the call has no performance lost: Since the member pointer is passed as a template parameter, the call can be inlined as usual.
Does the const-ness of Bar affect the const-ness of your Foo? Or are you just doing that because there's no way to distinguish the const Bar* vs Bar* versions? If this latter is the case, I'd just have one bar() that returned a Bar* and be done with it. Afterall, something that takes a const Bar* can take a Bar* just fine, anyway. Don't forget, the const-ness of the bar() methods is about Foo, not about Bar.
Also, if you offer both const- and non-const Bar* and are happy to have Foo be non-const, willy-nilly, then the constness of your Foo (and thus the bar() methods) obviously isn't that important, you're just filling in const and non-const versions for the heck of it. In which case, if you allow a non-const version, again just offer /only/ that one, consumers of const will happily take a non-const. Just, the way you've got const and non-const going there, it looks like it doesn't make much difference to the program.
If you want const Foo objects to be able to return both const and non-const Bars, then just make bar() const and return a non-const Bar.
I don't know, I'd have to see more to give a real answer.
Think through though, which const-nesses are truly connected, and whether your program needs both const Foos and non-const Foos. Allowing all possibilities willy-nilly, it seems like you haven't thought through what the program requires.
One picture worth more than 10 thousand words, so:
const Item*
Storage::SearchItemBySomething( const Something &something ) const
{
const Item* pData = NULL;
// Algorythm for searching.
return pData;
}
Item*
Storage::SearchItemBySomething( const Something &something )
{
return (Item*) ((const Storage*)this)->_SearchItemBySomething(something);
}
Copied and altered from actual code. General idea is that you implement your const method and you write the non const method which uses the first. You need to cast the "this" pointer to "const" and you need to cast away the "const" from the returning "const Item*".
You can replace the C-style casts to C++ style casts
My personal feeling is that if you are doing this a lot, there is something a bit suspect in your design. On the occasions I have had to do something similar, I've usually made the thing being accessed by the methods mutable.
The answer I came up with myself after bending my mind a bit. However, I think I can improve it using the ideas from litb's answer, which I'll post later. So my solution so far looks like this:
class ConstOverloadAdapter {
protected:
// methods returning pointers
template<
typename R,
typename T >
R* ConstOverload(
const R* (T::*mf)(void) const) {
return const_cast< R* >(
(static_cast< const T* >(this)->*mf)());
}
// and similar templates for methods with parameters
// and/or returning references or void
};
class Bar;
class Foo : public ConstOverloadAdapter {
public:
const Bar* bar() const {
/* implementation */ }
Bar* bar(void* = 0) { // the dummy void* is only needed for msvc
// since it cannot distinguish method overloads
// based on cv-type. Not needed for gcc or comeau.
return ConstOverload(&Foo::bar); }
};
I've also felt this pain before -- in essence, you're trying to tell the compiler that constness "propagates" through bar()
. Unfortunately, as far as I'm aware, there is no way to do this automatically... you'll just have to write the second version of the function by hand.