473,396 Members | 1,734 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Constness with standard containters of pointers

Hello,

I have some cuestions about constness with standard containers and
pointers.
Supose I have a list of pointers to some class B:

std::list< B * list;

I have readed that constness in std::list is the same that it is in C
arrays (const std::list makes const both the list and its content):

const std::list< B * constList;

I can't do:

constList->push_back(x);

But, is it still possible to call any non-const member funcion of B?:

void B::nonConstMemberFuncion();

As in:

constList.begin()->nonConstMemberFuncion();

a) It is possible. I don't want the user to add elements nor modify
any of the list. Which is the correct way of doing this?

const std::list< const B * const constList;
const std::list< const B * constList;

b) It is not possible. Then, I suppose that the next sentences are the
same:

const std::list < B constList;
const std::list < const B constList;

If I have the next class:

class A {
public:
std::list< B * & list() { return m_list; };
private:
std::list< B * m_list;
};

How I add a const version for A::list()?

What about if I have a smart_pointer instead of a C pointer?

const std::list< boost::shared_ptr< B constList =
something();
constList.begin()->nonConstMemberFunction();

Thanks.
Jun 27 '08 #1
13 1237
Hi!

Javier schrieb:
const std::list< const B * constList;
This is the way to go: "const B *" makes the "B" const, thus only const
functions can be called. The "const std::list" makes the list immutable,
thus you need to initialize it during construction.

Regards,
Frank
Jun 27 '08 #2
On 16 mayo, 17:20, Frank Birbacher <bloodymir.c...@gmx.netwrote:
>
const std::list< const B * constList;

This is the way to go: "const B *" makes the "B" const, thus only const
functions can be called. The "const std::list" makes the list immutable,
thus you need to initialize it during construction.
Ok, then if I have:

class A
{
public:
...
std::list< B * & getList() { return m_list; };
const std::list< const B * & getList() const
{ ...???... };

private:
std::list< B * m_list;
}

What should I write in the const version of "getList()"?

What about the conversion from "std::list< B * >" to "const std::list<
const B * >"... The second const changes the template argument so, is
it a different type?
Jun 27 '08 #3
Javier wrote:
[..] if I have:

class A
{
public:
...
std::list< B * & getList() { return m_list; };
const std::list< const B * & getList() const
{ ...???... };

private:
std::list< B * m_list;
}

What should I write in the const version of "getList()"?
You can't do much there. list<Tand list<const Tare different types
and cannot be converted into each other.
What about the conversion from "std::list< B * >" to "const std::list<
const B * >"... The second const changes the template argument so, is
it a different type?
Yep.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Jun 27 '08 #4
On 16 mai, 19:29, Victor Bazarov <v.Abaza...@comAcast.netwrote:
Javier wrote:
[..] if I have:
class A
{
public:
...
std::list< B * & getList() { return m_list; };
const std::list< const B * & getList() const
{ ...???... };
private:
std::list< B * m_list;
}
What should I write in the const version of "getList()"?
You can't do much there. list<Tand list<const Tare
different types and cannot be converted into each other.
That's not quite true. There's nothing to stop you from writing
something like:

std::list< B const* const A::getList() const
{
return std::list< B const* >( m_list.begin(), m_list.end() ) ;
}
What about the conversion from "std::list< B * >" to "const
std::list< const B * >"... The second const changes the
template argument so, is it a different type?
Yep.
But the STL is designed so that you can convert any type of sequence
to
any other type, as long as the contained elements are
convertible. I use this rather regularly for initializations,
but it works generally.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #5
James Kanze wrote:
On 16 mai, 19:29, Victor Bazarov <v.Abaza...@comAcast.netwrote:
>Javier wrote:
>>[..] if I have:
>> class A
{
public:
...
std::list< B * & getList() { return m_list; };
const std::list< const B * & getList() const
{ ...???... };
>> private:
std::list< B * m_list;
}
>>What should I write in the const version of "getList()"?
>You can't do much there. list<Tand list<const Tare
different types and cannot be converted into each other.

That's not quite true. There's nothing to stop you from writing
something like:

std::list< B const* const A::getList() const
{
return std::list< B const* >( m_list.begin(), m_list.end() ) ;
}
The OP had the function returning a _reference_.
>>What about the conversion from "std::list< B * >" to "const
std::list< const B * >"... The second const changes the
template argument so, is it a different type?
>Yep.

But the STL is designed so that you can convert any type of sequence
to
any other type, as long as the contained elements are
convertible. I use this rather regularly for initializations,
but it works generally.
I believe we use the term "convert" overloadedly here. Just as
much as one can "convert" an int into a C string...

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Jun 27 '08 #6
>
The OP had the function returning a _reference_.
That's the point. I want to return a reference to the member
attribute, but avoiding the user to make any changes to the container
and its content.
>
I believe we use the term "convert" overloadedly here. Just as
much as one can "convert" an int into a C string...
I want to "cast" not to create a new object and return it (if it is
possible). If not, the solution posted by James is enough for me (I
think):

std::list< B const* const A::getList() const
{
return std::list< B const* >( m_list.begin(), m_list.end() ) ;
}

Thanks all.
Jun 27 '08 #7
Javier wrote:
>
If I have the next class:

class A {
public:
std::list< B * & list() { return m_list; };
private:
std::list< B * m_list;
};

How I add a const version for A::list()?

What about if I have a smart_pointer instead of a C pointer?

const std::list< boost::shared_ptr< B constList =
something();
constList.begin()->nonConstMemberFunction();
The obvious question is, why? If the list is private to the class, why
would you want to expose it (as a constant or not) to users of the
class? This looks like poor design to me.

If the class is a wrapper for the list and you only want to grant const
access to users, how about providing that access through const iterators?

typedef std::list<B*>::const_iterator const_iterator;

const_iterator begin() const { return m_list.begin(); }
const_iterator end() const { return m_list.end(); }

--
Ian Collins.
Jun 27 '08 #8
Victor Bazarov a écrit :
James Kanze wrote:
On 16 mai, 19:29, Victor Bazarov <v.Abaza...@comAcast.netwrote:
Javier wrote:
[..] if I have:
> class A
{
public:
...
std::list< B * & getList() { return m_list; };
const std::list< const B * & getList() const
{ ...???... };
> private:
std::list< B * m_list;
}
>What should I write in the const version of "getList()"?
You can't do much there. list<Tand list<const Tare
different types and cannot be converted into each other.
That's not quite true. There's nothing to stop you from writing
something like:
std::list< B const* const A::getList() const
{
return std::list< B const* >( m_list.begin(), m_list.end() ) ;
}
The OP had the function returning a _reference_.
A const reference. But that's not the point. You can easily
convert a list< T to a list< T const >. You can also bind the
results of the conversion to a const reference (although in this
case, all that it will give you is a dangling reference). So
the probably solution in his case is to redeclare the function
to return an object, and use the conversion.
>What about the conversion from "std::list< B * >" to "const
std::list< const B * >"... The second const changes the
template argument so, is it a different type?
Yep.
But the STL is designed so that you can convert any type of
sequence to any other type, as long as the contained
elements are convertible. I use this rather regularly for
initializations, but it works generally.
I believe we use the term "convert" overloadedly here. Just
as much as one can "convert" an int into a C string...
Yes and no. There's a very real sense that the above is a
conversion, unlike the that of an int into a string. Like most
C++ conversions, however, the result is an rvalue---a copy of
the initial object, and not the object itself.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #9
On 17 mai, 02:46, Javier <jjeron...@gmail.comwrote:
The OP had the function returning a _reference_.
That's the point. I want to return a reference to the member
attribute, but avoiding the user to make any changes to the
container and its content.
The question then is why? Why not a copy?

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #10
Javier wrote:
Hello,

I have some cuestions about constness with standard containers and
pointers.
Supose I have a list of pointers to some class B:

std::list< B * list;

I have readed that constness in std::list is the same that it is in C
arrays (const std::list makes const both the list and its content):

const std::list< B * constList;

I can't do:

constList->push_back(x);

But, is it still possible to call any non-const member funcion of B?:

void B::nonConstMemberFuncion();

As in:

constList.begin()->nonConstMemberFuncion();

a) It is possible. I don't want the user to add elements nor modify
any of the list. Which is the correct way of doing this?

const std::list< const B * const constList;
const std::list< const B * constList;

b) It is not possible. Then, I suppose that the next sentences are the
same:

const std::list < B constList;
const std::list < const B constList;

If I have the next class:

class A {
public:
std::list< B * & list() { return m_list; };
private:
std::list< B * m_list;
};

How I add a const version for A::list()?
Your problem is that the list contains B* object and making those const
(e.g. by refering to the list via a const reference) does not make the
pointees const. You could fix that using a smart pointer that forwards
constness:
template < typename T >
class const_forwarding_ptr {

T * the_ptr;

public:

const_forwarding_ptr ( T * ptr )
: the_ptr ( ptr )
{}

T & operator* ( void ) {
return ( *the_ptr );
}

T const & operator* ( void ) const {
return ( *the_ptr );
}

T * operator-( void ) {
return ( the_ptr );
}

T const * operator-( void ) const {
return ( the_ptr );
}

operator T * ( void ) {
return ( the_ptr );
}

operator T const * ( void ) const {
return ( the_ptr );
}

};
#include <list>

int main ( void ) {
std::list< const_forwarding_ptr< int the_list;
int * i_ptr = new int ( 5 );
the_list.push_back( i_ptr );
*(*(the_list.begin())) = 6;
std::list< const_forwarding_ptr< int const & c_ref = the_list;
// the following line does not compile:
// *(*(c_ref.begin())) = 5;
}

What about if I have a smart_pointer instead of a C pointer?

const std::list< boost::shared_ptr< B constList =
something();
constList.begin()->nonConstMemberFunction();
Depends on the smart pointer. With regard to shared_ptr from C++0X, it does
not forward constness to the pointee but acts similar to a raw pointer.
Best

Kai-Uwe Bux
Jun 27 '08 #11
James Kanze wrote:
On 17 mai, 02:46, Javier <jjeron...@gmail.comwrote:
>>The OP had the function returning a _reference_.
>That's the point. I want to return a reference to the member
attribute, but avoiding the user to make any changes to the
container and its content.

The question then is why? Why not a copy?

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
I'm assuming you've probably already anticipated this reply and have an
answer ready, but I'll oblige by putting my foot in it anyway... :-)

"Because if n is the size of the list, making a copy is a Theta(n)
operation, whereas simply returning a reference (were it possible in
this case) would have been constant time."

It could be argued though that you should consider the real reason for
returning the list when doing this sort of thing: if you just want
clients of the class to be able to iterate over the list, then you might
be better off returning an iterator (*) than the list itself (thus
avoiding exposing an aspect of the class's implementation). This allows
you to get round the type problem with the list, since for a list
element type T, you can convert from a T* to a const T* without any
problems.

Regards,
Stu

(*) I don't necessarily mean "iterator" in the sense of a C++-style
iterator here - it's just a generic term for something which will let
you iterate over all the elements of the list.
Jun 27 '08 #12
On 17 mai, 11:44, Stuart Golodetz
<sgolod...@NdOiSaPlA.pMiPpLeExA.ScEomwrote:
James Kanze wrote:
On 17 mai, 02:46, Javier <jjeron...@gmail.comwrote:
>The OP had the function returning a _reference_.
That's the point. I want to return a reference to the member
attribute, but avoiding the user to make any changes to the
container and its content.
The question then is why? Why not a copy?
I'm assuming you've probably already anticipated this reply
and have an answer ready, but I'll oblige by putting my foot
in it anyway... :-)
"Because if n is the size of the list, making a copy is a
Theta(n) operation, whereas simply returning a reference (were
it possible in this case) would have been constant time."
In other words, you've already tried this solution, and found it
too slow.

The question then might be why you are using std::list, and not
std::vector or std::deque? If you can use one of them,
particularly std::vector, I think you'll find that your
performance problem disappears. It's true that there will be n
copies, but copying a pointer is ridiculously cheap; what's
causing the slowdown is probably the fact that std::list does an
allocation as well for each copy.

Another alternative---probably the best in the long run, at any
rate, is to define what the user wants to do with this list, and
provide an interface directly in your object for doing it. If
nothing else, you can easily provide iterators into it, by
wrapping the std::list<>::iterator. (You might have a look at
boost::iterator for this. If nothing else, it will save some
typing.)
It could be argued though that you should consider the real
reason for returning the list when doing this sort of thing:
if you just want clients of the class to be able to iterate
over the list, then you might be better off returning an
iterator (*) than the list itself (thus avoiding exposing an
aspect of the class's implementation). This allows you to get
round the type problem with the list, since for a list element
type T, you can convert from a T* to a const T* without any
problems.
Exactly. The real question is more along the lines of: what
does the user do with the list (or the reference to the list)
that it gets? And wouldn't it be sensible to make this possible
directly from the interface of your class, so that you don't
(directly or indirectly) expose the internals of your object,
i.e. the fact that it uses an std::list<>.
(*) I don't necessarily mean "iterator" in the sense of a
C++-style iterator here - it's just a generic term for
something which will let you iterate over all the elements of
the list.
For better or for worse, we're stuck with the C++ style
iterators. It's what C++ programmers know best. So while I
greatly prefer a GoF iterator, and will always provide an
interface for it when reasonably possible, any iterator which
might be used in generic code, or might be used by client code,
will support the STL idiom, and any code that I write which
might end up being generic (and thus need to iterate over an STL
container) will use the STL iterators, even when my iterators
support a simpler and more intuitive idiom.

The problems with the STL iterators are well known, and
Boost::iterator goes a long way in compensating for them. In
the case where you are simply wrapping another iterator, to
change the return type of operator*(), of course, it's fairly
easy... to screw up:-). An input iterator is trivial, but the
operator* of a forward iterator must return a reference, so you
have to cache the converted value somewhere. (Technically, I
think that even that is wrong, since the reference is required
to be in the container. But if the iterator doesn't allow
mutation, it's a silly requirement. Still, it does mean that
&*c.begin() != &*c.begin().)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #13
James Kanze wrote:
On 17 mai, 11:44, Stuart Golodetz
<sgolod...@NdOiSaPlA.pMiPpLeExA.ScEomwrote:
>James Kanze wrote:
>>On 17 mai, 02:46, Javier <jjeron...@gmail.comwrote:
The OP had the function returning a _reference_.
>>>That's the point. I want to return a reference to the member
attribute, but avoiding the user to make any changes to the
container and its content.
>>The question then is why? Why not a copy?
>I'm assuming you've probably already anticipated this reply
and have an answer ready, but I'll oblige by putting my foot
in it anyway... :-)
>"Because if n is the size of the list, making a copy is a
Theta(n) operation, whereas simply returning a reference (were
it possible in this case) would have been constant time."

In other words, you've already tried this solution, and found it
too slow.

The question then might be why you are using std::list, and not
std::vector or std::deque? If you can use one of them,
particularly std::vector, I think you'll find that your
performance problem disappears. It's true that there will be n
copies, but copying a pointer is ridiculously cheap; what's
causing the slowdown is probably the fact that std::list does an
allocation as well for each copy.
Oops, missed the important fact that it was a container of pointers,
sorry. In mitigation, my comment only referred to the asymptotic
complexity of the copying approach and not the speed in practice. And I
would still eschew it because (a) (as we both seem to agree) the
iterator approach below is better and (b) (to my mind) there's something
icky about copying a container every time you want to iterate over it
without changing it. There are some occasions when it could be slow, as
well, such as when n is very large and the getList call is made within
(e.g.) a nested loop.
Another alternative---probably the best in the long run, at any
rate, is to define what the user wants to do with this list, and
provide an interface directly in your object for doing it. If
nothing else, you can easily provide iterators into it, by
wrapping the std::list<>::iterator. (You might have a look at
boost::iterator for this. If nothing else, it will save some
typing.)
>It could be argued though that you should consider the real
reason for returning the list when doing this sort of thing:
if you just want clients of the class to be able to iterate
over the list, then you might be better off returning an
iterator (*) than the list itself (thus avoiding exposing an
aspect of the class's implementation). This allows you to get
round the type problem with the list, since for a list element
type T, you can convert from a T* to a const T* without any
problems.

Exactly. The real question is more along the lines of: what
does the user do with the list (or the reference to the list)
that it gets? And wouldn't it be sensible to make this possible
directly from the interface of your class, so that you don't
(directly or indirectly) expose the internals of your object,
i.e. the fact that it uses an std::list<>.
>(*) I don't necessarily mean "iterator" in the sense of a
C++-style iterator here - it's just a generic term for
something which will let you iterate over all the elements of
the list.

For better or for worse, we're stuck with the C++ style
iterators. It's what C++ programmers know best. So while I
greatly prefer a GoF iterator, and will always provide an
interface for it when reasonably possible, any iterator which
might be used in generic code, or might be used by client code,
will support the STL idiom, and any code that I write which
might end up being generic (and thus need to iterate over an STL
container) will use the STL iterators, even when my iterators
support a simpler and more intuitive idiom.

The problems with the STL iterators are well known, and
Boost::iterator goes a long way in compensating for them. In
the case where you are simply wrapping another iterator, to
change the return type of operator*(), of course, it's fairly
easy... to screw up:-). An input iterator is trivial, but the
operator* of a forward iterator must return a reference, so you
have to cache the converted value somewhere. (Technically, I
think that even that is wrong, since the reference is required
to be in the container. But if the iterator doesn't allow
mutation, it's a silly requirement. Still, it does mean that
&*c.begin() != &*c.begin().)
Hmm, I haven't actually taken a look at Boost::iterator - will check it
out, thanks :-)

Stu
Jun 27 '08 #14

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: Martin Magnusson | last post by:
I have a typedef which looks like this: typedef std::list< const S* const > seq_type; I also have a class with a private field: seq_type m_Sequence; When calling the following method
1
by: Richard Hayden | last post by:
Hi, I understand such pointers as 'const int* const ip' and 'const int* ip' etc., but I'm getting confused when seeing things like 'const int* const* ip' (i.e. with two or more asterisks)....
6
by: Marc | last post by:
T x; T foo(T, T); bind1st(ptr_fun(foo), x) creates a function object that takes an argument of type T const&. This does not work if T is already a reference type like int const&. So my first...
7
by: Gonçalo Rodrigues | last post by:
Hi all, I have a class, call it Object of heap-allocated objects. They are managed via a smart pointer class Ref (actually a template class but that does not matter for the problem). Object...
8
by: block111 | last post by:
I'm a student in a university and today we had a final exam in c++. Basicly, I was amazed by the amount of lame and invalid examples/questions. There was a question that in practice works but is a...
6
by: Querejeto | last post by:
Hello: Is it possible to detect programmatically the constness of a member function when it is called? That is, I would like to see a generic implementation (i.e. it does not depend on the...
2
by: MaxMax | last post by:
I have a problem of constness. struct MyS { int x; } void MyFunc(MyS* pA) { }
14
by: PengYu.UT | last post by:
In the following program, I want an iterator contain pointer pointing to constant object not const pointer. If it is possible would you please let me know how to do it? #include...
2
by: Laurent Deniau | last post by:
I am looking for the "cleanest" way to cast away the constness of a pointee in C89, something like const_cast<T*>() in C++. Actually, I am using: #define CONST_CAST(typename,value) \ (((union {...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.