tags:

views:

134

answers:

5

I would like to know if its possible to call a non-const member function from a const member function. In the example below First gives a compiler error. I understand why it gives an error, I would like to know if there is a way to work around it.

class Foo
{
   const int& First() const
   {
         return Second();
   }

   int& Second()
   {
        return m_bar;
   }

   int m_bar;
}

I don't really want to discuss the wisdom of doing this, I'm curious if its even possible.

+6  A: 

It is possible:

const int& First() const 
{ 
    return const_cast<Foo*>(this)->Second(); 
}

int& Second() { return m_bar; }

I wouldn't recommend this; it's ugly and dangerous (any use of const_cast is dangerous).

It's better to move as much common functionality as you can into helper functions, then have your const and non-const member functions each do as little work as they need to.

In the case of a simple accessor like this, it's just as easy to return m_bar; from both of the functions as it is to call one function from the other.

James McNellis
Jeremy W. Sherman
@Jeremy: How do you suppose we return a non-const reference to a member, then?
GMan
@GMan: Touché. You can't without declaring `m_bar` as a `mutable int`. @Fred Larson's recommendation is quite good, but the direction it leads - writing every blessed accessor twice, once `const`-modified and once not - is not so cheery.
Jeremy W. Sherman
@Jeremy: In most cases (not all, but most), it isn't necessary to have a non-const accessor (at least that's been my experience). Even if you do need both, since you have to write both functions anyway, writing the `return m_bar;` in both of them isn't usually too much of an additional burden.
James McNellis
the issue was inheritance of a zip iterator interface in a const version of the zip iterator, so it isn't as simple as the example suggests
Steve
+11  A: 
return (const_cast<Foo*>(this))->Second();

Then cry, quietly.

Adam Wright
A: 

By the definition of const, a function should not modify the state of an object. But if it calls another non-const member, the object's state might get changed, so it's disallowed.

I know you said you didn't want to hear about this, but I think it's important for others that happen upon the question.

Mark Ransom
+2  A: 

Overload on const:

const int& Second() const
{
    return m_bar;
}

You can add this method and keep the original non-const version.

Fred Larson
A: 

iterators are similar in this and make an interesting study.

const iterators are often the base for 'non const' iterators, and you will often find const_cast<>() or C style casts used to discard const from the base class with accessors in the child.

Edit: Comment was

I have a zip iterator where the const one inherits from the non-const

This would generally be the wrong inheritence structure (if your saying what I think you are), the reason being that children should not be less restrictive than parents.

say you had some algorithm taking your zip iterator, would it be appropriate to pass a const iterator to a non const ?

if you had a const container, could only ask it for a const iterator, but then the const iterator is derived from an iterator so you just use the features on the parent to have non const access.

Here is a quick outline of suggested inheritence following the traditional stl model

class ConstIterator: 
    public std::_Bidit< myType, int, const myType *, const mType & >
{
  reference operator*() const { return m_p; }
}

class Iterator : public ConstIterator 
{
  typedef ConstIterator _Mybase;
  // overide the types provided by ConstIterator
  typedef myType * pointer;
  typedef myType & reference;

  reference operator*() const
  { 
    return ((reference)**(_Mybase *)this);
  }
}

typedef std::reverse_iterator<ConstIterator> ConstReverseIterator;
typedef std::reverse_iterator<Iterator> ReverseIterator;
Greg Domjan
I tend to write iterators as template classes and used type traits to manipulate the constness of the member functions; it avoids the need for `const_cast` and makes the code easier to read.
James McNellis
Sounds interesting, though does it allow for using iterators where you might use const_iterator? I'm making this original observation on the stl(s) I've seen - which is mainly to ones with MSVC.
Greg Domjan
this was actually my use case for this. I have a zip iterator where the const one inherits from the non-const. @James, could you answer this question with the type traits answer? I'm really interested in not doing this if possible
Steve
@Steve: I'll try and dig something up; I don't have my PC with all my projects on it at the moment. The general approach is to create a single class template `template <typename T> class iterator_impl` and then in the container that is using it provide a `typedef iterator_impl<const T> const_iterator` and `typedef iterator_impl<T> iterator`. You can then inside of the `iterator_impl` template extract the constness from `T` and set all the return types and pointers and such appropriately. Is that what you're looking for? (I just want to make sure I understand exactly what you need).
James McNellis