Constness of the container of shared_ptr and the constness of it elements

P

PengYu.UT

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 <boost/shared_ptr.hpp>
#include <vector>
#include <iterator>
#include <iostream>

class trial {
public:
void const_fun() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void non_const_fun() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};

int main(){
std::vector<boost::shared_ptr<trial> > v;
v.push_back(boost::shared_ptr<trial>(new trial));
{
std::vector<boost::shared_ptr<trial> >::iterator it = v.begin();
(*it)->const_fun();
(*it)->non_const_fun();
}
{
std::vector<boost::shared_ptr<trial> >::const_iterator it =
v.begin();
(*it)->const_fun();
(*it)->non_const_fun();//want a const trial, this function should
not be called.
}
{
std::vector<boost::shared_ptr<const trial> >::iterator it =
v.begin();//compile error
(*it)->const_fun();
(*it)->non_const_fun();
}

std::vector<boost::shared_ptr<const trial> > v_const = v;//error,
//is there any conversion of
this kind?

return EXIT_SUCCESS;
}
 
R

Roland Pibinger

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?

No. Standard containers are for values only, not for pointers (neither
real nor "smart").

Best wishes,
Roland Pibinger
 
I

Ian Collins

Roland said:
No. Standard containers are for values only, not for pointers (neither
real nor "smart").
Care to elaborate? Containers are often used for both pointers and
smart pointers.
 
D

Daniel T.

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?

You have to wrap the vector in a class of your own that prohibits such
use.
#include <boost/shared_ptr.hpp>
#include <vector>
#include <iterator>
#include <iostream>

class trial {
public:
void const_fun() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void non_const_fun() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};

int main(){
std::vector<boost::shared_ptr<trial> > v;
v.push_back(boost::shared_ptr<trial>(new trial));
{
std::vector<boost::shared_ptr<trial> >::iterator it = v.begin();
(*it)->const_fun();
(*it)->non_const_fun();
}
{
std::vector<boost::shared_ptr<trial> >::const_iterator it =
v.begin();
(*it)->const_fun();
(*it)->non_const_fun();//want a const trial, this function should
not be called.

That function should be called. The const_iterator means that the
pointer value itself is const, not the trial object it points to. IE you
can change the trial object, but you can't replace it with a different
trial object.
}
{
std::vector<boost::shared_ptr<const trial> >::iterator it =
v.begin();//compile error
(*it)->const_fun();
(*it)->non_const_fun();
}

std::vector<boost::shared_ptr<const trial> > v_const = v;//error,
//is there any conversion of
this kind?

No, nor would there be if you were using bald pointers.
 
P

PengYu.UT

Daniel said:
You have to wrap the vector in a class of your own that prohibits such
use.


That function should be called. The const_iterator means that the
pointer value itself is const, not the trial object it points to. IE you
can change the trial object, but you can't replace it with a different
trial object.


No, nor would there be if you were using bald pointers.

If I have some function which can accept a vector of objects and it
doesn't change these objects, I have to define the function accept a
vector of objects (not const objects), because the answer to the above
question is "NO". Is it a drawback of the STL contain? Could it be
improved?
 
D

Daniel T.

If I have some function which can accept a vector of objects and it
doesn't change these objects, I have to define the function accept a
vector of objects (not const objects), because the answer to the above
question is "NO". Is it a drawback of the STL contain? Could it be
improved?

It's not a drawback of the STL containers. It is a proper and necessary
requirement. The object that the container is going to be destroying is
the one that is (and should be) const when the container is const. If
the container is holding pointers, then *that* is what is, and should
be, const.
 
R

red floyd

Roland said:
No. Standard containers are for values only, not for pointers (neither
real nor "smart").
=

Really, Roland? How would you manage a container of polymorphic objects
(classic case in point, a list or vector of Shapes).
 
R

red floyd

red said:
Really, Roland? How would you manage a container of polymorphic objects
(classic case in point, a list or vector of Shapes).

Follow-up. You can store pointers (raw or smart) in a Standard
container. In fact, that's the only way to maintain a container of
polymorphic objects.

The thing is that if you store raw pointers in a container, then *you*
are responsible for managing the lifetime of those pointers -- on a
recent project, we were using raw pointers (legacy code), and I made a
big deal during design reviews that pointer ownership and lifetime
maintenance had to be specified in the design (we were storing them in
containers).

If you use a smart pointer, then the smart pointer manages the lifetime
of the pointer, and you don't have to deal with it.
 
R

Roland Pibinger

Care to elaborate? Containers are often used for both pointers and
smart pointers.

'Value semantics' is one of the central characteristics of STL. All
containers, iterators and algorithms are designed for values only. You
may also use pointers as template arguments but they cause various
problems: clumsiness in usage, no 'const correctness' (see above),
algorithm mismatch (algorithms refer to pointers, not pointed-to
objects), ...
A container for pointers needs a design that is especially made for
pointers. Many library vendors provide (non-Standard) containers for
pointers, see e.g. CTypedPtrArray (MFC), QPtrVector (Qt), ... IMO, the
lack of appropriate containers for pointers is one of the major
reasons for the low acceptance of STL in the real world.

Best wishes,
Roland Pibinger
 
R

Roland Pibinger

red floyd wrote:
Follow-up. You can store pointers (raw or smart) in a Standard
container. In fact, that's the only way to maintain a container of
polymorphic objects.

Not the only way. You can store polymorphic objects in any container
that is appropriate for polymorphic objects. Just the Standard
containers are not.
The thing is that if you store raw pointers in a container, then *you*
are responsible for managing the lifetime of those pointers

Containment an ownership management are two different tasks. A
container for pointers should be first and foremost a container, not
an object manager.
-- on a
recent project, we were using raw pointers (legacy code), and I made a
big deal during design reviews that pointer ownership and lifetime
maintenance had to be specified in the design (we were storing them in
containers).

The rule of thumb is simple: Release resources in destructors. WRT
containers for pointers this means that the destructor of the
containing object shall delete the pointed-to objects in the
container.
A general-purpose container for pointers shall not and cannot delete
any objects (since it doesn't know how they have been created).
If you use a smart pointer, then the smart pointer manages the lifetime
of the pointer, and you don't have to deal with it.

The "smart" pointer solution means one resource manager (which
dynamically allocates a ref-counter) for each pointed-to object.
Moreover, "smart" pointers cause many unsmart problems. I would avoid
them.

Best wishes,
Roland Pibinger
 
M

mlimber

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 <boost/shared_ptr.hpp>
#include <vector>
#include <iterator>
#include <iostream>

class trial {
public:
void const_fun() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void non_const_fun() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};

int main(){
std::vector<boost::shared_ptr<trial> > v;
v.push_back(boost::shared_ptr<trial>(new trial));
{
std::vector<boost::shared_ptr<trial> >::iterator it = v.begin();
(*it)->const_fun();
(*it)->non_const_fun();
}
{
std::vector<boost::shared_ptr<trial> >::const_iterator it =
v.begin();
(*it)->const_fun();
(*it)->non_const_fun();//want a const trial, this function should
not be called.
}
{
std::vector<boost::shared_ptr<const trial> >::iterator it =
v.begin();//compile error
(*it)->const_fun();
(*it)->non_const_fun();
}

std::vector<boost::shared_ptr<const trial> > v_const = v;//error,
//is there any conversion of
this kind?

return EXIT_SUCCESS;
}

There have been several debates on the Boost development group about
whether or not to give smart pointers "deep constness" (see for
instance http://thread.gmane.org/gmane.comp.lib.boost.devel/95836).
This issue is particularly important when considering const member
functions that can change the pointees of its class' const members. For
instance,

class Foo
{
int *pi_;
std::vector<int> vi_;
public:
// ...

void Bar() const
{
pi_[0] = 0; // Ok, but unintended?
vi_[0] = 0; // Error! vi_ is const here
}
};

Boost smart pointers, like raw pointers, allow modification of the
pointees, but some standard containers like std::vector don't. Because
the goal for boost::shared_ptr was to be "as close as possible to a raw
pointer, but no closer", the final decision was to make it not support
deep constness.

To change the behavior of a smart pointer for members, you might use a
wrapper class like this:

template <typename T>
class deep_shared_ptr
{
public:
// Constructors
deep_shared_ptr() {}

template<typename Y>
deep_shared_ptr( Y* const y ) : m_ptr( y ) {}

template<typename Y>
deep_shared_ptr( boost::shared_ptr<Y>& that ) : m_ptr( that ) {}

template<typename Y>
deep_shared_ptr( deep_shared_ptr<Y>& that ) : m_ptr( that ) {}


// The usual operations
T* operator->() { return m_ptr.operator->(); }
T& operator*() { return m_ptr.operator*(); }


// The unusual: make pointee const if pointer is const
T const* operator->() const { return m_ptr.operator->(); }
T const& operator*() const { return m_ptr.operator*(); }


// Reset pass-throughs
template<typename Y>
void reset( Y* const y )
{ m_ptr.reset( y ); }

template<typename Y>
void reset( boost::shared_ptr<Y>& that )
{ m_ptr.reset( that ); }

template<typename Y>
void reset( deep_shared_ptr<Y>& that )
{ m_ptr.reset( that.Get() ); }


private:
boost::shared_ptr<T> m_ptr;

// Disable copying from const deep pointers
deep_shared_ptr( const deep_shared_ptr& );
deep_shared_ptr( const boost::shared_ptr<T>& );

template<typename Y>
deep_shared_ptr& operator=( const deep_shared_ptr<Y>& );

template<typename Y>
deep_shared_ptr& operator=( const boost::shared_ptr<Y>& );
};

Unfortunately, as is reflected in the disabled functions, deep pointers
of this kind do not support normal copy semantics since they don't
accept a copy from a const deep_shared_ptr and can thus cannot be used
in standard containers. They can be handy as members, however.

Cheers! --M
 
I

Ian Collins

Roland said:
'Value semantics' is one of the central characteristics of STL. All
containers, iterators and algorithms are designed for values only. You
may also use pointers as template arguments but they cause various
problems: clumsiness in usage, no 'const correctness' (see above),
algorithm mismatch (algorithms refer to pointers, not pointed-to
objects), ...
A container for pointers needs a design that is especially made for
pointers. Many library vendors provide (non-Standard) containers for
pointers, see e.g. CTypedPtrArray (MFC), QPtrVector (Qt), ... IMO, the
lack of appropriate containers for pointers is one of the major
reasons for the low acceptance of STL in the real world.
So what disqualifies a smart pointer from being an object that can be
stored in a container?
 
R

Roland Pibinger

So what disqualifies a smart pointer from being an object that can be
stored in a container?

It compiles but it doesn't really work. For the reasons pointed out
above.

Best wishes,
Roland Pibinger
 
I

Ian Collins

Roland said:
It compiles but it doesn't really work. For the reasons pointed out
above.
Well smart pointers can help.

I'd expect containers to be used as simple storage with pointers, as you
say, the standard algorithms expect value semantics. It makes no sense
to apply std::sort to a vector of pointers, but a vector of pointers is
still preferable to a plain array of pointers.

If you wanted to use the object pointed to, you could provide the
appropriate operators on a smart pointer object.

You could also use a smart pointer to const type to enforce const
correctness.

Provided the user understands the limitations, standard containers are a
good home for (smart) pointers.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top