C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

const and non-const iterators in const member functions

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
John W. Wilkinson
Guest





PostPosted: Tue Sep 16, 2003 7:00 am    Post subject: const and non-const iterators in const member functions Reply with quote




How can I make the method Company::find, below, const?

The problem is that I have to declare 'i' to be a const iterator when 'find'
is
const. I cannot then safely convert it to non-const to return it.

I do not understand precisely what is going on. It seems that in a const
method the
non-const version of 'begin' is not visible.

struct employee {
std::string name_;
// int salary_;
// etc ...
};

bool name_is ( const employee &employee, const std::string &name ) {
return employee.name_ == name;
}

class Company
{
...
private:
typedef std::vector<employee> employees;

employees e;

employees::iterator find ( const std::string name ) const
{
employees::const_iterator i = e.begin();
for ( ; i != e.end(); ++i ) {
if ( name_is ( *i, name ) ) break;
}
return i; // cannot convert from 'const_iterator' to
'iterator'

// return std::find_if ( e.begin(), e.end(),
// boost::bind(name_is,_1,name) ); //
alternative but same problem
}
};

thanks
John




[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Daniel Spangenberg
Guest





PostPosted: Tue Sep 16, 2003 1:44 pm    Post subject: Re: const and non-const iterators in const member functions Reply with quote



Hello John Wilkinson,

"John W. Wilkinson" schrieb:

Quote:
How can I make the method Company::find, below, const?

The problem is that I have to declare 'i' to be a const iterator when 'find'
is
const. I cannot then safely convert it to non-const to return it.

I do not understand precisely what is going on. It seems that in a const
method the
non-const version of 'begin' is not visible.

The problem is that your const find function returns an iterator and **not**
a const_iterator. This contradicts to the function policy, because:

- The functions says: I am const so don't modify me!
- The return value to one of the class members says: You are allowed to
modify me.

What do you want?

In case of a const find function your return type must be const_iterator (You
may
note that the function internals already use const_iterators), otherwise the
function should be non-const.

A third possibility is: You do not make employees a member of that
class. In this case you just store a (non-const) reference or pointer on
this container in the class and can declare you find function being const
but in the same moment return a mutable iterator. This delegates the
const/mutable problem to the external assignment ofthe actual employees
container.

A fourth possible solution is to say: The employees member is used
for some internal caching mechanism and so the find function is **logical**
const (although it modifies the class internals physically). In this case
you could decide to declare the employees member to be "mutable" using
this very keyword. To my personal opinion this last possibility seems
**not** to describe well the responsibility of this container in your class,
so I would not use it in this situation.


Hope that helps,

Daniel Spangenberg




[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Daniel Pfeffer
Guest





PostPosted: Tue Sep 16, 2003 8:53 pm    Post subject: Re: const and non-const iterators in const member functions Reply with quote



"John W. Wilkinson" <jwilk (AT) freeuk (DOT) com> wrote

Quote:

How can I make the method Company::find, below, const?

The problem is that I have to declare 'i' to be a const iterator when
'find'
is
const. I cannot then safely convert it to non-const to return it.

I do not understand precisely what is going on. It seems that in a const
method the
non-const version of 'begin' is not visible.

struct employee {
std::string name_;
// int salary_;
// etc ...
};

bool name_is ( const employee &employee, const std::string &name ) {
return employee.name_ == name;
}

class Company
{
...
private:
typedef std::vector<employee> employees;

employees e;

employees::iterator find ( const std::string name ) const
{
employees::const_iterator i = e.begin();
for ( ; i != e.end(); ++i ) {
if ( name_is ( *i, name ) ) break;
}
return i; // cannot convert from 'const_iterator' to
'iterator'

// return std::find_if ( e.begin(), e.end(),
// boost::bind(name_is,_1,name) ); //
alternative but same problem
}
};

The problem is that in a const method - all members of the class are
constant. Inside Company::find, this is as if e were declared as 'const
employees e;'

One solution would be to cast e to a non-const form. As we know that e is
not _really_ const, we can portably write:

employees::iterator find ( const std::string name ) const
{
employees* pe = const_cast<employees *>(e);
employees::iterator i = pe->begin();

for ( ; i != pe->end(); ++i ) {
if ( name_is ( *i, name ) ) break;
}

return i;
}


HTH,
Daniel Pfeffer



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Werner Salomon
Guest





PostPosted: Wed Sep 17, 2003 9:35 am    Post subject: Re: const and non-const iterators in const member functions Reply with quote

"John W. Wilkinson" <jwilk (AT) freeuk (DOT) com> wrote

Quote:
How can I make the method Company::find, below, const?

The problem is that I have to declare 'i' to be a const iterator when 'find'
is
const. I cannot then safely convert it to non-const to return it.
Hi John,


that's right. Save converting a const_iterator to a iterator is not
possible.

Quote:
I do not understand precisely what is going on. It seems that in a const
method the
non-const version of 'begin' is not visible.
If there is a const company and the employees are part of the company,

You don't want that someone outside can change an employee. With a
valid iterator to an employee he can do it.
So it is ok, not to give an iterator to an inner member outside of a
const object.

Quote:

struct employee {
std::string name_;
// int salary_;
// etc ...
};

bool name_is ( const employee &employee, const std::string &name ) {
return employee.name_ == name;
}

class Company
{
...
private:
typedef std::vector<employee> employees;

employees e;

employees::iterator find ( const std::string name ) const
{
employees::const_iterator i = e.begin();
for ( ; i != e.end(); ++i ) {
if ( name_is ( *i, name ) ) break;
}
return i; // cannot convert from 'const_iterator' to
'iterator'

// return std::find_if ( e.begin(), e.end(),
// boost::bind(name_is,_1,name) ); //
alternative but same problem
}
};
Try:

employees::const_iterator find( const std::string& name ) const {
return std::find_if ( e.begin(), e.end(),
boost::bind( &name_is, _1, name ) );
}
if You need a non-const find too, add a non-const-methode
employees::iterator find( const std::string& name ) {
return std::find_if ( e.begin(), e.end(),
boost::bind( &name_is, _1, name ) );
}
You can have both methods at the same time in Your class. Have a look
at the implementation of std::vector<>::begin in Your STL.

Greetings
Werner

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Todd Johnson
Guest





PostPosted: Wed Sep 17, 2003 9:39 am    Post subject: Re: const and non-const iterators in const member functions Reply with quote

"John W. Wilkinson" <jwilk (AT) freeuk (DOT) com> wrote

Quote:
How can I make the method Company::find, below, const?

The problem is that I have to declare 'i' to be a const iterator when 'find'
is
const. I cannot then safely convert it to non-const to return it.

I do not understand precisely what is going on. It seems that in a const
method the
non-const version of 'begin' is not visible.
The compiler is being your friend here. Your find method is

conceptually similar to begin. Consider the following:

void func2(const vector<int>& vec)
{
*vec.begin() = 0;
}

vector<int> vec;
vec.push_back(1);
func(vec); // shouldn't change vec
assert(vec[0] == 1);

Any reasonable compiler will refuse to compile this.
If 'vector::begin() const' where able to return a non-const iterator
(what you are trying to do with your find function), this code would
compile without a whimper from the compiler.

I think your problem is conceptual. It appears to you that your find
function should be const since IT doesn't modify the container. That
is a misconception. You need two versions of the find function: one
const and one non-const, each returning the appropriate kind of
iterator.

-Todd

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
John W. Wilkinson
Guest





PostPosted: Wed Sep 17, 2003 9:45 am    Post subject: Re: const and non-const iterators in const member functions Reply with quote


"Daniel Spangenberg" <dsp (AT) bdal (DOT) de> wrote

Quote:
Hello John Wilkinson,

"John W. Wilkinson" schrieb:

How can I make the method Company::find, below, const?

The problem is that I have to declare 'i' to be a const iterator when
'find'
is
const. I cannot then safely convert it to non-const to return it.

I do not understand precisely what is going on. It seems that in a const
method the
non-const version of 'begin' is not visible.

The problem is that your const find function returns an iterator and
**not**
a const_iterator. This contradicts to the function policy, because:

- The functions says: I am const so don't modify me!
- The return value to one of the class members says: You are allowed to
modify me.

Yes, but the problem I had with this was that Company::find does not
actually modify its object. It only allows a callee to modify it. On
reflection this turns out to amount to much the same thing. Returning a
non-cost pointer from a const method is forbidden to prevent such things as:

struct s
{
int i;
int *get_i ( ) const { return &i; } // not legal
};

void f ( const s &s1 )
{
int *p = s1.get_i(); // allowed because get_i is const
*p = 2; // const object modified
}

The unfortunate corollary to this is that you no longer get an error if you
unintentionally implement such a method as Company::find with an algorithm
that modifies the object's contents.

Quote:
What do you want?

In case of a const find function your return type must be const_iterator
(You
may
note that the function internals already use const_iterators), otherwise
the
function should be non-const.

A third possibility is: You do not make employees a member of that
class. In this case you just store a (non-const) reference or pointer on
this container in the class and can declare you find function being const
but in the same moment return a mutable iterator. This delegates the
const/mutable problem to the external assignment ofthe actual employees
container.

A fourth possible solution is to say: The employees member is used
for some internal caching mechanism and so the find function is
**logical**
const (although it modifies the class internals physically). In this case
you could decide to declare the employees member to be "mutable" using
this very keyword. To my personal opinion this last possibility seems
**not** to describe well the responsibility of this container in your
class,
so I would not use it in this situation.

Hope that helps,

Thanks, I agree making the employees member to be mutable is not a good
idea.

John




[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Siemel Naran
Guest





PostPosted: Wed Sep 17, 2003 7:54 pm    Post subject: Re: const and non-const iterators in const member functions Reply with quote

"Daniel Pfeffer" <pfefferd (AT) hotmail (DOT) co.il> wrote in message

Quote:
class Company
{
typedef std::vector<employee> employees;

employees e;

employees::iterator find ( const std::string name ) const
{
employees::const_iterator i = e.begin();
for ( ; i != e.end(); ++i ) {
if ( name_is ( *i, name ) ) break;
}
return i; // cannot convert from 'const_iterator' to
'iterator'

One solution would be to cast e to a non-const form. As we know that e is
not _really_ const, we can portably write:

This statement is not correct. We may have declared an instance of class
Company on the stack as const --

int main() {
const Company company;
}

in which case company.e is really const, and it is undefined behavior to
cast away constness of it (though in practice with the current
implementation of std::vector it should be OK anyway).

I think the best solution is to either make the find function non-const, or
leave it as const and return a const_iterator. (Though I'd probably return
a const employee& and throw an exception if no employee found -- returning
an iterator exposes the implementation somewhat.)


Quote:
employees::iterator find ( const std::string name ) const
{
employees* pe = const_cast<employees *>(e);
employees::iterator i = pe->begin();

for ( ; i != pe->end(); ++i ) {
if ( name_is ( *i, name ) ) break;
}

return i;
}

It is however safe to implement the non-const find in terms of the const
find with the use of const_cast to cast away const.

--
+++++++++++
Siemel Naran


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Falk Tannhäuser
Guest





PostPosted: Thu Sep 18, 2003 1:30 pm    Post subject: Re: const and non-const iterators in const member functions Reply with quote

Siemel Naran wrote:
Quote:
I think the best solution is to either make the find function non-const, or
leave it as const and return a const_iterator. (Though I'd probably return
a const employee& and throw an exception if no employee found -- returning
an iterator exposes the implementation somewhat.)

employees::iterator find ( const std::string name ) const
{
employees* pe = const_cast<employees *>(e);
employees::iterator i = pe->begin();

for ( ; i != pe->end(); ++i ) {
if ( name_is ( *i, name ) ) break;
}

return i;
}

It is however safe to implement the non-const find in terms of the const
find with the use of const_cast to cast away const.
For cases like this, wouldn't it be useful to have the possibility of

overloading the 'const_cast' operator for iterators? Today, it works
only for pointers and references...

Falk

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Daniel Pfeffer
Guest





PostPosted: Fri Sep 19, 2003 9:58 am    Post subject: Re: const and non-const iterators in const member functions Reply with quote

"Siemel Naran" <SiemelNaran (AT) REMOVE (DOT) att.net> wrote

Quote:
"Daniel Pfeffer" <pfefferd (AT) hotmail (DOT) co.il> wrote in message

class Company
{
typedef std::vector<employee> employees;

employees e;

employees::iterator find ( const std::string name ) const
{
employees::const_iterator i = e.begin();
for ( ; i != e.end(); ++i ) {
if ( name_is ( *i, name ) ) break;
}
return i; // cannot convert from 'const_iterator' to
'iterator'

One solution would be to cast e to a non-const form. As we know that e
is
not _really_ const, we can portably write:

This statement is not correct. We may have declared an instance of class
Company on the stack as const --

int main() {
const Company company;
}

in which case company.e is really const, and it is undefined behavior to
cast away constness of it (though in practice with the current
implementation of std::vector it should be OK anyway).


In the general case - you are correct. In this particular case (a Company
class), I very much doubt that you would want to define a company with an
unchanging list of employees.


Daniel Pfeffer



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Siemel Naran
Guest





PostPosted: Sun Sep 21, 2003 10:13 am    Post subject: Re: const and non-const iterators in const member functions Reply with quote

"Daniel Pfeffer" <pfefferd (AT) hotmail (DOT) co.il> wrote

Quote:
"Siemel Naran" <SiemelNaran (AT) REMOVE (DOT) att.net> wrote in message

int main() {
const Company company;
}

in which case company.e is really const, and it is undefined behavior to
cast away constness of it (though in practice with the current
implementation of std::vector it should be OK anyway).


In the general case - you are correct. In this particular case (a Company
class), I very much doubt that you would want to define a company with an
unchanging list of employees.

Our program may not represent the life of the company. Perhaps we
have a program that reads a company from a file, prints a report, then
exits. Suppose we achieve the first step, namely reading the company from a
file, through a one argument constructor. Then the company may then be
declared const.

int main() {
const Company company("c:company.txt");
company.print(std::cout);
}

--
+++++++++++
Siemel Naran



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Joshua Lehrer
Guest





PostPosted: Mon Sep 22, 2003 11:26 am    Post subject: Re: const and non-const iterators in const member functions Reply with quote

"John W. Wilkinson" <jwilk (AT) freeuk (DOT) com> wrote

Quote:
The unfortunate corollary to this is that you no longer get an error if you
unintentionally implement such a method as Company::find with an algorithm
that modifies the object's contents.


My solutionm to this is to forward the work to a global template
method that is templated both on the container type (so you can pass
in either a const container or a non-const container), and templated
on the iterator type to use (so you can pass in either iterator or
const_iterator).

Now, the two versions of find will each share the same implementation.
If your implementation attempts to modify a value, the const version
will fail to compile. Thus, you are guaranteed that neither will
modify a value, and you are not duplicating code.

untested:

template <typename I, typename C>
I find_impl(C & container, int value) {
if (container.empty()) return container.end();
I result;
//guts here
return result;
}

Container::iterator Container::find(int value) {
return find_impl<Container::iterator>(*this);
}

Container::const_iterator Container::find(int value) const {
return find_impl<Container::const_iterator>(*this);
}

joshua lehrer
factset research systems
NYSE:FDS

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Joshua Lehrer
Guest





PostPosted: Mon Sep 22, 2003 6:35 pm    Post subject: Re: const and non-const iterators in const member functions Reply with quote

"John W. Wilkinson" <jwilk (AT) freeuk (DOT) com> wrote

Quote:
The unfortunate corollary to this is that you no longer get an error if you
unintentionally implement such a method as Company::find with an algorithm
that modifies the object's contents.


My solutionm to this is to forward the work to a global template
method that is templated both on the container type (so you can pass
in either a const container or a non-const container), and templated
on the iterator type to use (so you can pass in either iterator or
const_iterator).

Now, the two versions of find will each share the same implementation.
If your implementation attempts to modify a value, the const version
will fail to compile. Thus, you are guaranteed that neither will
modify a value, and you are not duplicating code.

untested:

template <typename I, typename C>
I find_impl(C & container, int value) {
if (container.empty()) return container.end();
I result;
//guts here
return result;
}

Container::iterator Container::find(int value) {
return find_impl<Container::iterator>(*this);
}

Container::const_iterator Container::find(int value) const {
return find_impl<Container::const_iterator>(*this);
}

joshua lehrer
factset research systems
NYSE:FDS

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Siemel Naran
Guest





PostPosted: Tue Jan 06, 2004 2:18 am    Post subject: Re: const and non-const iterators in const member functions Reply with quote

"Falk Tannhäuser" <falk.tannhauser (AT) crf (DOT) canon.fr> wrote in message
Quote:
Siemel Naran wrote:

It is however safe to implement the non-const find in terms of the const
find with the use of const_cast to cast away const.

For cases like this, wouldn't it be useful to have the possibility of
overloading the 'const_cast' operator for iterators? Today, it works
only for pointers and references...

Not needed if you reverse what I say to "implement the const find in terms
of the non-const find".

Class::iterator Class::find();

inline
Class::const_iterator Class::find() const {
return const_cast<Class*>(this)->find();
}

There is required to be an implicit conversion from iterator to
const_iterator.

--
+++++++++++
Siemel Naran


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.