Topic: Defect Report: constness of iterator arguments to container member functions has unintended consequences


Author: Dave Abrahams <abrahams@mediaone.net>
Date: 1999/07/01
Raw View
Here's a simple and typical example problem which is currently very=20
difficult or impossible to solve without the change proposed below.
=A0
Wrap a standard container C in a class W which allows clients to find and
read (but not modify) a subrange of (C.begin(), C.end()]. The only
modification clients are allowed to make to elements in this subrange is =
to
erase them from C through the use of a member function of W.

The problem is that it is the constness of the container which should
control whether it can be modified through a member function such as
erase(), not the constness of the iterators. The iterators only serve to
give positioning information.

Proposed solution: all non-const iterator parameters to container member
functions should be changed (or overloaded) to accept const_iterator
parameters.

This change has already been widely tested in the SGI STL, for example.

In 21.3/6 change:

      iterator insert(iterator p, charT c);
      void     insert(iterator p, size_type n, charT c);
      template<class InputIterator>
        void insert(iterator p, InputIterator first, InputIterator last);
      basic_string& erase(size_type pos =3D 0, size_type n =3D npos);
      iterator erase(iterator position);
      iterator erase(iterator first, iterator last);

to:

      iterator insert(const_iterator p, charT c);
      void     insert(const_iterator p, size_type n, charT c);
      template<class InputIterator>
        void insert(const_iterator p, InputIterator first, InputIterator
last);
      basic_string& erase(size_type pos =3D 0, size_type n =3D npos);
      iterator erase(const_iterator position);
      iterator erase(const_iterator first, const_iterator last);

and:

      basic_string& replace(iterator i1, iterator i2, const basic_string&
str);
      basic_string& replace(iterator i1, iterator i2, const charT* s,
                            size_type n);
      basic_string& replace(iterator i1, iterator i2, const charT* s);
      basic_string& replace(iterator i1, iterator i2,
                            size_type n, charT c);
      template<class InputIterator>
        basic_string& replace(iterator i1, iterator i2,
                              InputIterator j1, InputIterator j2);

to:

      basic_string& replace(const_iterator i1, const_iterator i2, const
basic_string& str);
      basic_string& replace(const_iterator i1, const_iterator i2, const
charT* s,
                            size_type n);
      basic_string& replace(const_iterator i1, const_iterator i2, const
charT* s);
      basic_string& replace(const_iterator i1, const_iterator i2,
                            size_type n, charT c);
      template<class InputIterator>
        basic_string& replace(const_iterator i1, const_iterator i2,
                              InputIterator j1, InputIterator j2);

in 21.3.5.4 change:

  iterator insert(iterator p, charT c);

to:
  iterator insert(const_iterator p, charT c);

and:
  void insert(iterator p, size_type n, charT c);

to:
  void insert(const_iterator p, size_type n, charT c);

and:
  template<class InputIterator>
    void insert(iterator p, InputIterator first, InputIterator last);
to:
  template<class InputIterator>
    void insert(const_iterator p, InputIterator first, InputIterator last=
);

in   21.3.5.5  change:

  iterator erase(iterator p);

to:
  iterator erase(const_iterator p);

and:
  iterator erase(iterator first, iterator last);

to:
  iterator erase(const_iterator first, const_iterator last);

in 21.3.5.6 change:

  basic_string& replace(iterator i1, iterator i2, const basic_string& str=
);

to:
  basic_string& replace(const_iterator i1, const_iterator i2, const
basic_string& str);

and:
  basic_string&
    replace(iterator i1, iterator i2, const charT* s, size_type n);
to:
  basic_string&
    replace(const_iterator i1, const_iterator i2, const charT* s, size_ty=
pe
n);

and:
  basic_string& replace(iterator i1, iterator i2, const charT* s);
to:
  basic_string& replace(const_iterator i1, const_iterator i2, const charT=
*
s);

and:
  basic_string& replace(iterator i1, iterator i2, size_type n,
                        charT c);
to:
  basic_string& replace(const_iterator i1, const_iterator i2, size_type n=
,
                        charT c);
and:
  template<class InputIterator>
    basic_string& replace(iterator i1, iterator i2,
                          InputIterator j1, InputIterator j2);
to:
  template<class InputIterator>
    basic_string& replace(const_iterator i1, const_iterator i2,
                          InputIterator j1, InputIterator j2);

in 23.1.1/9 change:

      template <class InputIterator>          // such as insert()
      rt fx1(iterator p, InputIterator f, InputIterator l);
to:
      template <class InputIterator>          // such as insert()
      rt fx1(const_iterator p, InputIterator f, InputIterator l);
and:
      template <class InputIterator>          // such as replace()
      rt fx3(iterator i1, iteraror i2, InputIterator f, InputIterator l);
to:
      template <class InputIterator>          // such as replace()
      rt fx3(const_iterator i1, const_iteraror i2, InputIterator f,
InputIterator l);

....
You get the idea. There are lots of changes. You can find all of the plac=
es
by grepping for:

[(,][ \t\n]*iterator=20
---
[ 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: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/07/01
Raw View
Dave Abrahams wrote:
>
> Here's a simple and typical example problem which is currently very
> difficult or impossible to solve without the change proposed below.
>
> Wrap a standard container C in a class W which allows clients to find and
> read (but not modify) a subrange of (C.begin(), C.end()]. The only
> modification clients are allowed to make to elements in this subrange is to
> erase them from C through the use of a member function of W.
>
> The problem is that it is the constness of the container which should
> control whether it can be modified through a member function such as
> erase(), not the constness of the iterators. The iterators only serve to
> give positioning information.

I didn't had to think much to write the following:

template <class Sequence>
class warper {
    Sequence sequ;
public:
    typedef typename Sequence::value_type const value_type;

    class iterator {
        friend warper;
        typename Sequence::iterator pos;
        iterator (typename Sequence::iterator where);
    public:
        value_type& operator* () const { return *pos; }
        iterator& operator++ ()
        { ++pos; return *this; }
    };

    iterator insert (iterator where, value_type& what)
    { return iterator (sequ.insert (where.pos, what)); }
};

--

Valentin Bonnard
---
[ 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: "Ed Brey" <brey@afd.mke.etn.com>
Date: 1999/07/05
Raw View
Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote in message
news:377B3D97.12FC@wanadoo.fr...
VB> Dave Abrahams wrote:
VB> >
VB> > Here's a simple and typical example problem which is currently very
VB> > difficult or impossible to solve without the change proposed below.
VB> >
VB> > Wrap a standard container C in a class W which allows clients to find
and
VB> > read (but not modify) a subrange of (C.begin(), C.end()]. The only
VB> > modification clients are allowed to make to elements in this subrange is
to
VB> > erase them from C through the use of a member function of W.
VB> >
VB> > The problem is that it is the constness of the container which should
VB> > control whether it can be modified through a member function such as
VB> > erase(), not the constness of the iterators. The iterators only serve to
VB> > give positioning information.
VB>
VB> I didn't had to think much to write the following:
VB>
VB> template <class Sequence>
VB> class warper {
VB>     Sequence sequ;
VB> public:
VB>     typedef typename Sequence::value_type const value_type;
VB>
VB>     class iterator {
VB>         friend warper;
VB>         typename Sequence::iterator pos;
VB>         iterator (typename Sequence::iterator where);
VB>     public:
VB>         value_type& operator* () const { return *pos; }
VB>         iterator& operator++ ()
VB>         { ++pos; return *this; }
VB>     };
VB>
VB>     iterator insert (iterator where, value_type& what)
VB>     { return iterator (sequ.insert (where.pos, what)); }
VB> };

This seems backwards.  A client of warper (I assume your implemention of
wrapper class W in the problem statement) can only insert.  The problem
desires the client to be able to read and remove (basically everything but
insert).
---
[ 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: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/07/07
Raw View
Ed Brey wrote:
EB>
BE> Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote in message
EB> VB> I didn't had to think much to write the following:
EB> VB>
EB> VB> template <class Sequence>
EB> VB> class warper {
EB> VB>     Sequence sequ;
EB> VB> public:
EB> VB>     typedef typename Sequence::value_type const value_type;

...

EB> VB>     iterator insert (iterator where, value_type& what)
EB> VB>     { return iterator (sequ.insert (where.pos, what)); }
EB> VB> };
EB>
EB> This seems backwards.

Perhaps

> A client of warper (I assume your implemention of
> wrapper class W in the problem statement) can only insert.

Yes

> The problem
> desires the client to be able to read and remove (basically everything but
> insert).

That's the ``dual'' problem, which is perfectely
equivalent.

--

Valentin Bonnard


[ 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              ]