问题
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 a Bar somewhere */ }
Bar* bar() {
return const_cast< Bar* >(
static_cast< const Foo* >(this)->bar());
}
};
for lots of methods like bar()
. Writing these non-const methods which call the const ones by hand is tedious; besides, I feel I am repeating myself – which makes me feel bad.
What can I do to alleviate this task? (Macros and code generators are not allowed.)
Edit: Besides litb's solution I also like my own. :)
回答1:
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.
回答2:
Use following trick:
class Bar;
class Foo {
public:
Bar* bar() {
// non-const case
/* code that does something */
}
const Bar* bar() const {
return This().bar(); // use non-const case
}
private:
//trick: const method returns non-const reference
Foo & This() const { return const_cast<Foo &>(*this); }
};
Note it is possible to use unique function This
for any const/non-const functions.
Alternative solution without static_cast (but I prefer the first one):
class Bar;
class Foo {
public:
const Bar* bar() const { /* code that does something */ }
Bar* bar() { return const_cast<Bar*>(cthis().bar()); } // use const case
private:
const Foo & cthis() const { return *this; }
};
回答3:
You can do something like this:
class Bar;
class Foo {
public:
const Bar* bar() const { return getBar(); }
Bar* bar() {
return getBar();
}
private:
Bar* getBar() const {/* Actual code */ return NULL;}
};
回答4:
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.
回答5:
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.
回答6:
FYI - The code posted by the OP is the preferred method given in Scott Meyers' "Effective C++ - Third Edition". See Item #3.
回答7:
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); }
};
回答8:
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.
回答9:
Your code implies that the const bar() is actually creating and returning a new Bar, and I find that peculiar if you're doing that lot.
For me a bigger problem is that constness in a member function is limited to reference and non-pointer members. The moment you have (non-const) pointer members, a const function can change them even though it claims to be const on the box.
If your Bar instance is a member variable, consider returning a reference instead.
回答10:
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.
回答11:
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
- static_cast<Item*>( ... )
- const_cast<const Storage*>(this)
来源:https://stackoverflow.com/questions/1333849/how-to-generate-a-non-const-method-from-a-const-method