 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
John W. Wilkinson Guest
|
Posted: Tue Sep 16, 2003 7:00 am Post subject: const and non-const iterators in const member functions |
|
|
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
|
Posted: Tue Sep 16, 2003 1:44 pm Post subject: Re: const and non-const iterators in const member functions |
|
|
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
|
Posted: Tue Sep 16, 2003 8:53 pm Post subject: Re: const and non-const iterators in const member functions |
|
|
"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
|
Posted: Wed Sep 17, 2003 9:35 am Post subject: Re: const and non-const iterators in const member functions |
|
|
"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
|
Posted: Wed Sep 17, 2003 9:39 am Post subject: Re: const and non-const iterators in const member functions |
|
|
"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
|
Posted: Wed Sep 17, 2003 9:45 am Post subject: Re: const and non-const iterators in const member functions |
|
|
"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
|
Posted: Wed Sep 17, 2003 7:54 pm Post subject: Re: const and non-const iterators in const member functions |
|
|
"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
|
Posted: Thu Sep 18, 2003 1:30 pm Post subject: Re: const and non-const iterators in const member functions |
|
|
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
|
Posted: Fri Sep 19, 2003 9:58 am Post subject: Re: const and non-const iterators in const member functions |
|
|
"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
|
Posted: Sun Sep 21, 2003 10:13 am Post subject: Re: const and non-const iterators in const member functions |
|
|
"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
|
Posted: Mon Sep 22, 2003 11:26 am Post subject: Re: const and non-const iterators in const member functions |
|
|
"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
|
Posted: Mon Sep 22, 2003 6:35 pm Post subject: Re: const and non-const iterators in const member functions |
|
|
"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
|
Posted: Tue Jan 06, 2004 2:18 am Post subject: Re: const and non-const iterators in const member functions |
|
|
"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 |
|
 |
|
|
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
|
|