Topic: Overloading const functions


Author: "Paul Sinnett" <pauls@Probe.co.uk>
Date: 1998/08/22
Raw View
I might be wrong here but to get a const iterator from the const begin
function shouldn't you use:

const Object object;
Object::const iterator c = object.begin();

otherwise the compiler will use the non-const object begin function and then
cast the result to a const iterator.


      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/08/25
Raw View
Paul Sinnett wrote:
>
> I might be wrong here but to get a const iterator from the const begin
> function shouldn't you use:

I guess you meant a const_iterator; a const iterator is possible, but
practically useless (operator++ is a non-const member function!)

>
> const Object object;
> Object::const iterator c = object.begin();
          const_iterator

In this case, it is a syntax error...

>
> otherwise the compiler will use the non-const object begin function and then
> cast the result to a const iterator.

Another way is to use the following Const template:

template<class T> inline T const& Const(T const& t) { return t; }

Then you can write:

Object object;
Object::const_iterator c = Const(object).begin();


      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Chad Gatesman <chadg@redrose.net>
Date: 1998/08/20
Raw View
The begin() member function is typically handled like this:

iterator begin() { return (link_type)((*node).next); }
const_iterator begin() const { return (link_type)((*node).next); }

My question is, what decides on which function will be used:

Object object;
Object::iterator i = object.begin();
Object::const_iterator c = object.begin();

My understanding is that functions cannot be overloaded by their return
type.  So the only way both begin() members to exist is the ability to
overload by the fact that one member function is const and the other
isn't.

If this is truely the case, what makes the second iterator declaration
use the const_iterator begin() const function instead of the default
iterator begin() function?


The reason I ask such a question is that I'm trying to write a "iterator
wrapper" for iterators that return a pair.  The reason I am doing this
is because I want the iterator to simply return the value part of the
pair (without the key).

Here is my iterator:


template<class Iterator, class T, class Ref, class Ptr>
struct SimpleIterator
{
 typedef SimpleIterator<Iterator, T, T&, T*>  iterator;
 typedef SimpleIterator<Iterator, T, const T&, const T*>
const_iterator;
 typedef SimpleIterator<Iterator, T, Ref, Ptr>  self;

 typedef T value_type;
 typedef size_t size_type;
 typedef Ptr pointer;
 typedef Ref reference;
 typedef Iterator link_type;

 link_type node;

 SimpleIterator(const link_type x) : node(x) { }
 SimpleIterator(void) { }
 SimpleIterator(const iterator &x) : node(x.node) { }

 bool operator==(const self& x) const { return node == x.node; }
 bool operator!=(const self& x) const { return node != x.node; }
 reference operator*() const { return (*node).second; }
 pointer operator->() const { return &(*node).second; }

 self& operator++()
 {
  ++node;
   return *this;
  }

 self operator++(int)
 {
    self tmp = *this;
    ++*this;
    return tmp;
 }

 self& operator--()
 {
  --node;
  return *this;
 }

 self operator--(int)
 {
  self tmp = *this;
  --*this;
  return tmp;
 }
};



Here is a case that I'm trying to use the iterator for a class:


class ObjectList
{
 public:
  // Typedefs
  typedef hash_map<int,Object *,hash<int>,equal_to<int> > object_hash;
  typedef object_hash::iterator  object_iterator;
  typedef object_hash::const_iterator  const_object_iterator;
  typedef SimpleIterator<object_iterator, Object *, Object *&, Object
**>  iterator;
  typedef SimpleIterator<const_object_iterator, Object *, const Object
*&, const Object **>  const_iterator;

  ObjectList(void);
  virtual ~ObjectList(void);


  // Iterator Functions
  const_iterator begin(void) const { return this->_Areas.begin(); }
  iterator begin(void) { return this->_Areas.begin(); }
  const_iterator end(void) const { return this->_Areas.end(); }
  iterator end(void) { return this->_Areas.end(); }

[CLIP]

 private:

  hash_map<int,Object *,hash<int>,equal_to<int> >  _Objects;

};



The regular ObjectList::iterator seems to be fine, but if I try to use a
ObjectList::const_iterator I have problems:

ObjectList::const_iterator i;

// List has been previously defined
for (i = List.begin(); i != AreaList.end(); ++i)
    /* Do some stuff */;


I get:

no match for
`SimpleIterator<__hashtable_const_iterator<pair<const int,Object
*>,int,hash<int>,select1st<pair<const int,Object *>
>,equal_to<int>,__default_alloc_template<false,0> >,Object *,const
Object *&,const Object **> & =
SimpleIterator<__hashtable_iterator<pair<const int,Object
*>,int,hash<int>,select1st<pair<const int,Object *>
>,equal_to<int>,__default_alloc_template<false,0> >,Object *,Object
*&,Object **>'

candidates are: SimpleIterator<__hashtable_const_iterator<pair<const
int,Object *>,int,hash<int>,select1st<pair<const int,Object *>
>,equal_to<int>,__default_alloc_template<false,0> >,Object *,const
Object *&,const Object **>::operator =(const
SimpleIterator<__hashtable_const_iterator<pair<const int,Object
*>,int,hash<int>,select1st<pair<const int,Object *>
>,equal_to<int>,__default_alloc_template<false,0> >,Object *,const
Object *&,const Object **> &)


The only thing I don't like about templates; the long errors :(
I will simpify this so you don't have to try and decipher it:

no match for
ObjectList::const_iterator & = ObjectList::iterator

canidates are:
ObjectList::const_iterator::operator =(const ObjectList::const_iterator
&)




Author: sbnaran@fermi.ceg.uiuc.edu (Siemel Naran)
Date: 1998/08/21
Raw View
On 20 Aug 1998 16:02:41 GMT, Chad Gatesman <chadg@redrose.net> wrote:

>My understanding is that functions cannot be overloaded by their return
>type.  So the only way both begin() members to exist is the ability to
>overload by the fact that one member function is const and the other
>isn't.

Right.  There's a hack to use operator functions in a struct to get
overloading on return type, but don't do this!


>Object object;
>Object::iterator i = object.begin();
>Object::const_iterator c = object.begin();

>If this is truely the case, what makes the second iterator declaration
>use the const_iterator begin() const function instead of the default
>iterator begin() function?

If object is non-const, then the begin() that returns iterator will
be called.  If object is const, then the begin() that returns
const_iterator will be called.

Suppose object is non-const.  Then in LINE3 object.begin() you get
an iterator.  If there is no way to convert an iterator into a
const_iterator, then the compiler gives you an error.

One way to get this conversion (if you want it, that is) is to
define a conversion copy constructor:
  const_iterator::const_iterator(const iterator&);

OTOH, you can just do
  const Object& const_object=object; // object has type Object
  Object::const_iterator c = const_object.begin();





>The reason I ask such a question is that I'm trying to write a "iterator
>wrapper" for iterators that return a pair.  The reason I am doing this
>is because I want the iterator to simply return the value part of the
>pair (without the key).

What does this have to do with const?  Are you lazy to do lots of
typing?  It is a good reason: more typing means more chance for
error.



>template<class Iterator, class T, class Ref, class Ptr>
>struct SimpleIterator

Very complicated template args.  Not sure if references allowed
for template args.  Consider this:


// just some struct/class
struct MyClass
{
     int first;
     double second;
};
// you want a container of this struct: ie: list<MyClass>

// fancier way
typedef pair<int,double> MyClass;


// your iterator's operator* should return only the second arg
// so you want an extractor to extract the second arg:
struct SecondArg_of_MyClass
{
     typedef MyClass aggregate;
     typedef double& reference;
     typedef double* pointer;

     SecondArg() { }
     pointer get_pointer(aggregate& m) const { return &m.second; }

     //a static function also makes sense:
     //static pointer get_pointer(aggregate& m) { return &m.second; }
};

// fancier and nicer
struct MyClass : public pair<T1,T2>
{
     MyClass(const T1&, const T2&); // can also receive by value

     struct some_arg
     {
          typedef MyClass aggregate;
     };

     struct second_arg : some_arg // this is the extractor
     {
          typedef T2& reference;
          typedef T2* pointer;

          second_arg() { }
          pointer get_pointer(aggregate& a) { return &a.second; }
     };
};



// now make your iterator
template <class extract>
class extract_iterator
{
     public:
          typedef typename extract::aggregate aggregate;
          typedef typename extract::reference reference;
          typedef typename extract::pointer pointer;
          typedef list<aggregate>::iterator iterator;

     private:
          iterator d_iter;

     public:
          extract_iterator(iterator);
          pointer operator->() const {
               return extract().get_pointer(*d_iter);
               //return Extract::get_pointer(*d_iter); // static
function
          }
};


typedef extract_iterator<MyClass::second_arg> MyIterator;

// expanded out and without too many typedefs
// optimizations are marked

class MyIterator
{
     public:
          typedef list<MyClass>::iterator iterator;

     private:
          iterator d_iter;
          // note: *d_iter returns a MyClass&

     public:
          myiterator(iterator);
          double* operator->() const {
               //return second_arg().get_pointer(*d_iter); // original
               //the ctor and dtor calls optimized away
               //return & ( (*d_iter).second ); // after inlining
               return & ( d_iter->second ); // likely optimization
          }
};



Q: How would we generalize this if we wanted to extract any element?
That is, given a struct/class with data members {a,b,c,d,e,f} do we
really want to write extractors and const_extractors for each of the
six elements?




> typedef Iterator link_type;
> link_type node;
Should not be in the public interface.

> SimpleIterator(void) { }
Need this?


--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Ross Smith <ross.s@ihug.co.nz>
Date: 1998/08/21
Raw View
Chad Gatesman wrote in message <35DB9A94.1544167D@redrose.net>...
>
>The begin() member function is typically handled like this:
>
>iterator begin() { return (link_type)((*node).next); }
>const_iterator begin() const { return (link_type)((*node).next); }
>
>My question is, what decides on which function will be used:
>
>Object object;
>Object::iterator i = object.begin();
>Object::const_iterator c = object.begin();
>
>My understanding is that functions cannot be overloaded by their return
>type.  So the only way both begin() members to exist is the ability to
>overload by the fact that one member function is const and the other
>isn't.
>
>If this is truely the case, what makes the second iterator declaration
>use the const_iterator begin() const function instead of the default
>iterator begin() function?

It doesn't. The object is non-const, so it uses the non-const version of
begin(), and casts the iterator to a const_iterator. If you define an
iterator and a const_iterator for a container class, you need to supply
that conversion operation (either give const_iterator a constructor that
takes an iterator, or give iterator an "operator const_iterator()"). I
suspect the lack of such an operation is why your code is failing to do
what you expect.

--
Ross Smith ................................... mailto:ross.s@ihug.co.nz
.............. The Internet Group, Auckland, New Zealand ..............
  "Remember when we told you there was no future? Well, this is it."
                                                        -- Blank Reg




      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]