views:

1089

answers:

7

Let's say I have the following class X where I want to return access to an internal member:

class Z
{
    // details
};

class X
{
    std::vector<Z> vecZ;

public:
    Z& Z(size_t index)
    {
        // ...
        // massive amounts of code for
        // validating index

        Z& ret = vecZ[index];

        // even more code for determining that
        // the Z instance at index is *just*
        // the right sort of Z (a process which
        // involves calculating leap years in which
        // religious holidays fall on Tuesdays for
        // the next thousand years or so)
        // ...

        return ret;
    }
    const Z& Z(size_t index) const
    {
        // identical to non-const X::Z(), except
        // printed in a lighter shade of gray since
        // we're running low on toner by this point
        // ...
    }
};

The two member functions 'X::Z()' and 'X::Z() const' have identical code inside the braces. This is duplicate code and can cause maintenance problems for long functions with complex logic.

Is there a way to avoid this code duplication?

+13  A: 

Yes, it is possible to avoid the code duplication. You need to use the const member function to have the logic and have the non-const member function call the const member function and re-cast the return value to a non-const reference (or pointer if the functions returns a pointer):

class X
{
   std::vector<Z> vecZ;

public:
   const Z& Z(size_t index) const
   {
      // same really-really-really long access 
      // and checking code as in OP
      // ...
      return vecZ[index];
   }

   Z& Z(size_t index)
   {
      // One line. One ugly, ugly line - but just one line!
      return const_cast<Z&>( static_cast<const Z&>(*this).Z(index) );
   }

 #if 0 // A slightly less-ugly version
   Z& Z(size_t index)
   {
      // Two lines -- one cast. This is slightly less ugly but takes an extra line.
      const X& constMe = *this;
      return const_cast<Z&>( constMe.Z(index) );
   }
 #endif
};

NOTE: It is important that you do NOT put the logic in the non-const function and have the const-function call the non-const function -- it may result in undefined behavior. The reason is that a constant class instance gets cast as a non-constant instance. The non-const member function may accidentally modify the class, which the C++ standard states will result in undefined behavior.

Kevin
Wow... that's horrible. You just increased the amount of code, decreased the clarity, and added *two* stinkin' const_cast<>s. Perhaps you have an example in mind where this actually makes sense?
Shog9
I agree with Shog. I prefer the original!
Rob
Yes, that hideous beast is much worse than the code duplication.
17 of 26
Ok, you modified it to work on something other than a simple integer. The "duplicating" code is *still* more clear than the non-duplicating code.
Shog9
Hey don't ding this!, it may be ugly, but according to Scott Meyers, it is (almost) the correct way. See _Effective C++_, 3d ed, Item 3 under the heading "Avoiding duplication in const and non-cost member functions.
jwfearn
While I understand that the solution may be ugly, imagine that the code that determines what to return is 50 lines long. Then duplication is highly undesirable -- especially when you have to re-factor the code. I've encountered this many times in my career.
Kevin
@jwfearn -- Out of curiosity, what is incorrect? And how do Scott Meyers and I differ?
Kevin
Michael Burr
Got it. Thanks Mike!
Kevin
Steve Jessop
Thanks onebyone. Got it!
Kevin
A: 

This DDJ article shows a way using template specialization that doesn't require you to use const_cast. For such a simple function it really isn't needed though.

boost::any_cast (at one point, it doesn't any more) uses a const_cast from the const version calling the non-const version to avoid duplication. You can't impose const semantics on the non-const version though so you have to be very careful with that.

In the end some code duplication is okay as long as the two snippets are directly on top of each other.

Greg Rogers
The DDJ article seems to refer to iterators -- which isn't relevant to the question. Const-iterators are not constant data -- they are iterators that point to constant data.
Kevin
A: 

Typically, the member functions for which you need const and non-const versions are getters and setters. Most of the time they are one-liners so code duplication is not an issue.

Dima
That may be true most of the time. But there are exceptions.
Kevin
getters anyway, a const setter doesn't make much sense ;)
jwfearn
I meant that the non-const getter is effectively a setter. :)
Dima
+17  A: 
jwfearn
Nobody ever got fired for following Scott Meyers :-)
Steve Jessop
const_cast should almost never be used. It breaks const-ness. This is especialy true on embeeded systems where you have ROM. In general it's a bad idea to use const_cast.
witkamp
Ouch... you may already be in trouble if you're counting on const == ROM. Compilers just aren't good enough to prove such a strong assertion.
Adam Mitz
witkamp is correct that in general it's bad to use const_cast. This is a specific case where it isn't, as Meyers explains. @Adam: ROM => const is fine. const == ROM is obviously nonsense since anyone can cast non-const to const willy-nilly: it's equivalent to just choosing not to modify something.
Steve Jessop
In general I would suggest using const_cast instead of static_cast to add const since it prevents you from changing the type accidentally.
Greg Rogers
+7  A: 

A bit more verbose than Meyers, but I might do this:

class X {

    private:

    // This method MUST NOT be called except from boilerplate accessors.
    Z &_getZ(size_t index) const {
        return something;
    }

    // boilerplate accessors
    public:
    Z &getZ(size_t index)             { return _getZ(index); }
    const Z &getZ(size_t index) const { return _getZ(index); }
};

The private method has the undesirable property that it returns a non-const Z& for a const instance, which is why it's private. Private methods may break invariants of the external interface (in this case the desired invariant is "a const object cannot be modified via references obtained through it to objects it has-a").

Note that the comments are part of the pattern - _getZ's interface specifies that it is never valid to call it (aside from the accessors, obviously): there's no conceivable benefit to doing so anyway, because it's 1 more character to type and won't result in smaller or faster code. Calling the method is equivalent to calling one of the accessors with a const_cast, and you wouldn't want to do that either. If you're worried about making errors obvious (and that's a fair goal), then call it const_cast_getZ instead of _getZ.

By the way, I appreciate Meyers's solution. I have no philosophical objection to it. Personally, though, I prefer a tiny bit of controlled repetition, and a private method that must only be called in certain tightly-controlled circumstances, over a method that looks like line noise. Pick your poison and stick with it.

[Edit: Kevin has rightly pointed out that _getZ might want to call a further method (say generateZ) which is const-specialised in the same way getZ is. In this case, _getZ would see a const Z& and have to const_cast it before return. That's still safe, since the boilerplate accessor polices everything, but it's not outstandingly obvious that it's safe. Furthermore, if you do that and then later change generateZ to always return const, then you also need to change getZ to always return const, but the compiler won't tell you that you do.

That latter point about the compiler is also true of Meyers's recommended pattern, but the first point about a non-obvious const_cast isn't. So on balance I think that if _getZ turns out to need a const_cast for its return value, then this pattern loses a lot of its value over Meyers's. Since it also suffers disadvantages compared to Meyers's, I think I would switch to his in that situation. Refactoring from one to the other is easy -- it doesn't affect any other valid code in the class, since only invalid code and the boilerplate calls _getZ.]

Steve Jessop
I like it. When i get votes, i'll vote this up... :-0)
Shog9
This still has the problem that the thing you return may be constant for a constant instance of X. In that case, you still require a const_cast in _getZ(...). If misused by later developers, it can still lead to UB.If the thing that is being returned is 'mutable', then this is a good solution.
Kevin
When I said that '_getZ(...)' may be mis-used, I meant that if a future developer didn't understand that it was only to be used by the public functions and called it directly, but modified the value in a constant instance of X, then that could lead to UB. This is possible if not documented well.
Kevin
Any private function (heck, public ones too) can be mis-used by later developers, if they choose to ignore the BLOCK CAPITAL instructions on its valid use, in the header file and also in Doxygen etc. I can't stop that, and I don't consider it my problem since the instructions are easy to understand.
Steve Jessop
Steve Jessop
Sorry, by "reference I'm using to access X" I of course mean "reference I'm using to access an instance of X".
Steve Jessop
Gosh, these 300 char comments can come out sounding kind of abrasive, can't they? I'm not meaning to beat you down - _getZ *is* a bit icky, I just don't think it's icky in a way that matters. My comment is part of the pattern - readers won't find the method without seeing the comment.
Steve Jessop
Steve Jessop
I'm back. On reflection I think that if _getZ needs to do a const_cast, then this pattern is probably contra-indicated and it's best to switch back to Meyers's. I'm really shutting up now.
Steve Jessop
+2  A: 

How about moving the logic into a private method, and only doing the "get the reference and return" stuff inside the getters? Actually, I would be fairly confused about the static and const casts inside a simple getter function, and I'd consider that ugly except for extremely rare circumstances!

MP24
In order to avoid undefined behavior you still need a const_cast. See the answer by Martin York and my comment there.
Kevin
+1  A: 

You could also solve this with templates. This solution is slightly ugly (but the ugliness is hidden in the .cpp file) but it does provide compiler checking of constness, and no code duplication.

.h file:

#include <vector>

class Z
{
    // details
};

class X
{
    std::vector<Z> vecZ;

public:
    const std::vector<Z>& GetVector() const { return vecZ; }
    std::vector<Z>& GetVector() { return vecZ; }

    Z& GetZ( size_t index );
    const Z& GetZ( size_t index ) const;
};

.cpp file:

#include "constnonconst.h"

template< class ParentPtr, class Child >
Child& GetZImpl( ParentPtr parent, size_t index )
{
    // ... massive amounts of code ...

    // Note you may only use methods of X here that are
    // available in both const and non-const varieties.

    Child& ret = parent->GetVector()[index];

    // ... even more code ...

    return ret;
}

Z& X::GetZ( size_t index )
{
    return GetZImpl< X*, Z >( this, index );
}

const Z& X::GetZ( size_t index ) const
{
    return GetZImpl< const X*, const Z >( this, index );
}

The main disadvantage I can see is that because all the complex implementation of the method is in a global function, you either need to get hold of the members of X using public methods like GetVector() above (of which there always need to be a const and non-const version) or you could make this function a friend. But I don't like friends.

[Edit: removed unneeded include of cstdio added during testing.]

Andy Balaam
You can always make the complex implementation function a static member to gain access to the private members. The function need only be declared in the the class header file, the definition can reside in the class implementation file. It is, after all, part of the class implementation.
Charles Bailey
Aah yes good idea! I don't like the template stuff appearing in the header, but if since here it potentially makes the implemtation quite a lot simpler it is probably worth it.
Andy Balaam