Call nonconst member version from const

263 Views Asked by At

Possible Duplicate:
How do I remove code duplication between similar const and non-const member functions?

i have two members

A &B::GetA (int i)
{
    return *(m_C[m_AtoC[i]]);
}

const A &B::GetA (int i) const
{
    return *(m_C[m_AtoC[i]]);
}

for now i just duplicate code, but may be there exists nice way to do it. I certanly dont want to deal with type cast from const to non-const.

EDIT: So i want to call one member frm another to avoid code duplication.

6

There are 6 best solutions below

24
On BEST ANSWER

[Note the correction; sorry for getting it the wrong way round initially.]

This is an appropriate situation for using a const_cast, and it allows you to deduplicate code by forwarding the call from the non-const function to the corresponding const function:

A & B::GetA(int index) 
{
    return const_cast<A &>(static_cast<B const *>(this)->GetA(index));
}

It's important to note that this should only be done in one direction: You can implement the non-const method in terms of the constant one, but not the other way round: The constant call to GetA() gets a constant reference to the object in question, but since we have additional information that it's actually OK to mutate the object, we can safely cast away the constness from the result.

There's even a chapter on precisely this technique in Scott Meyer's Effective C++.

3
On

Assuming the bodies of GetA() and GetA() const are identical (which means GetA() doesn't modify *this), you can safely use one const_cast to implement the const version using the non-const one:

const A& B::GetA() const {
  return const_cast<B*>(this)->GetA();
}

The non-const GetA() doesn't modify the object, so calling it on a const B object is not undefined. The non-const reference returned by non-const GetA() is converted to a const& before being returned out of GetA() const, so there's no danger of undefined behaviour there either.

0
On

You can avoid const_cast with a little template metaprogramming.

// This meta function returns a const U if T is const, otherwise returns just U.
template <typename T, typename U>
make_same_const<T, U>
{
    typedef typename std::conditional<
            std::is_const<T>::value,
            typename std::add_const<U>::type,
            U
        >::type type;
};


// Generic version of a function. Constness of return type depends on
// constness of T.
// This is a static member template of B.
template <typename T>
make_same_const<T, A>::type& B::GetA(T& obj, int i)
{
    return *(obj.m_C[obj.m_AtoC[i]]);
}

A&       B::GetA(int i)       { return B::GetA(*this, i); }
A const& B::GetA(int i) const { return B::GetA(*this, i); }
10
On

How about

const A &B::GetA (int index) const
{
    return *(const_cast<B*>(this)->GetA(index));
}
2
On

You could do something like:

class B {
public:
    A& GetA (int index) { return GetA_impl(index); }
    const A& GetA (int index) const { return GetA_impl(index); }
private:
    A& GetA_impl (int index) const { return *(m_C[m_AtoC[i]]); }
};

I'm not sure it's really worth the effort in this case, but this can be useful if the potentially duplicated code gets more complicated.

0
On

IMO this isn't enough code (or complexity) to be worth de-duplicating.

As you can see in both the const_cast-based solutions, the cast expression is actually longer than the original code.

If you have a longer or more complex expression you're really worried about, though, please show it.