Container elements with const members

N

Nobody

The requirement that STL container elements have to be assignable is causing
me a problem.
Consider a class X which contains both const and non-const data members:

class X
{
public:
X(const std::string &rStrId, int x) : m_strId(strId), m_x(x)
{
}

private:
X& operator=(const X &rhs); // not allowed
X(const X& rhs); // not allowed

const std::string m_strId;
int m_x;
};

I know that because I have the data member, m_strId, declared as *const*, it
means that I cannot assign to it. This was OK as I didn't need to and
therefore made it private with no implementation.

Then I decided that I needed to have an STL vector of Xs.

X x(std::string("hello"), 43);
std::vector<X > xVector;
....
xVector.push_back(X(std::string("hello"), 43));

I know too that when using STL containers the standard says that the
contained objects must be copyable and assignable.

This is my dilemma. If I don't supply an assignment operator for X or I make
it private then it won't compile when I try to push an instance of X into
the vector.

I have considered the following options:

1) Make the string non const and let the compiler generate the assignment
operator. I don't want to do this as each instance of X is required to have
a unique string identifier, m_strId, which will not change throughout the
lifetime of the object.

2) Provide an assignment operator that only assigns the non const int m_x.
This would allow the possibility of assigning one X object to another and
the result not being equal.

Which is the best option of the two? Are there any other alternatives I
haven't considered which are better?

TIA

RP
 
S

Siemel Naran

Nobody said:
class X
{
public:
X(const std::string &rStrId, int x) : m_strId(strId), m_x(x)
{
}

private:
X& operator=(const X &rhs); // not allowed
X(const X& rhs); // not allowed

const std::string m_strId;
I know too that when using STL containers the standard says that the
contained objects must be copyable and assignable.
1) Make the string non const and let the compiler generate the assignment
operator. I don't want to do this as each instance of X is required to have
a unique string identifier, m_strId, which will not change throughout the
lifetime of the object.

Yes, your design should reflect your business requirements, not the
technical requirements on STL containers.
2) Provide an assignment operator that only assigns the non const int m_x.
This would allow the possibility of assigning one X object to another and
the result not being equal.

Yes, this also violates your business requirement.
Which is the best option of the two? Are there any other alternatives I
haven't considered which are better?

Use a container of pointers to X. Remember to delete the pointed to objects
when the container goes out of scope.

You could try smart pointers, like so:

std::vector<boost::shared_ptr<X> > d_container;
 
C

Claudio Jolowicz

On Wed, 21 Apr 2004, Nobody wrote:

[snip]
class X
{
public:
X(const std::string &rStrId, int x) : m_strId(strId), m_x(x)
{
}

private:
X& operator=(const X &rhs); // not allowed
X(const X& rhs); // not allowed

const std::string m_strId;
int m_x;
};
[snip]

X x(std::string("hello"), 43);
std::vector<X > xVector;
...
xVector.push_back(X(std::string("hello"), 43));

I know too that when using STL containers the standard says that the
contained objects must be copyable and assignable.
[snip]

I have considered the following options:

1) Make the string non const and let the compiler generate the assignment
operator. I don't want to do this as each instance of X is required to have
a unique string identifier, m_strId, which will not change throughout the
lifetime of the object.

2) Provide an assignment operator that only assigns the non const int m_x.
This would allow the possibility of assigning one X object to another and
the result not being equal.

Which is the best option of the two?

Neither option is correct if your code must guarantee that there is only
one instance for any string identifier, and the same string identifier
for a given object throughout the object's lifetime.
Are there any other alternatives I
haven't considered which are better?

Yes, store pointers in the vector. For example:

#include <vector>
#include <string>

class X
{
public:
X(const std::string &rStrId, int x)
: m_strId(rStrId), m_x(x)
{}

private:
X& operator=(const X &rhs); //not allowed
X(const X& rhs); //not allowed

const std::string m_strId;
int m_x;
};

class App
{
std::vector<X*> m_pvx;

public:
App()
: m_pvx()
{}

~App()
{
for (std::vector<X*>::iterator it = m_pvx.begin();
it != m_pvx.end();
++it)
delete *it;
}

void run()
{
m_pvx.push_back(new X(std::string("hello"), 43));
}
};

int main()
{
App().run();
}
 
T

Thorsten Ottosen

| On Wed, 21 Apr 2004, Nobody wrote:

|
| Yes, store pointers in the vector. For example:

[snip]

|
| class App
| {
| std::vector<X*> m_pvx;

^^^^^^^^^^^^^^^^^^^^^^

highly non-recommended. Use vector< shared_ptr<X> >.

br

Thorsten
 
S

Siemel Naran

Thorsten Ottosen said:
highly non-recommended. Use vector< shared_ptr<X> >.

Highly non-recommended by who? I think Claudio's original solution is fine.
It uses less memory than your version (a counted_ptr is about twice the size
as a regular pointer, plus add memory for the actual integers), though
granted this may sometimes pale in comparison to the sizeof(X).

To save on the typing, it would be nice to generalize the idea to a template
class pointer_container<T> that holds a container<T*>, and whose destructor
deletes the items.
 
D

Dave Moore

Siemel Naran said:
To save on the typing, it would be nice to generalize the idea to a template
class pointer_container<T> that holds a container<T*>, and whose destructor
deletes the items.

Hmm .. aren't there ownership issues inherent to this idea? Who is to
say that the elements in the container belong to it? Couldn't the
pointer-elements have been initialized from already existing objects,
for example the elements of another container<T*>? It seems to me
that this would only really be useful in the context of the boost
smart-pointers, where ownership is already clear, IOW

shared_ptr_container<T> for container<shared_ptr<T> >, when the
elements will be owned by the container and
weak_ptr_container<T> for container<weak_ptr<T> > when they will not.

I suppose you could do a similar thing without boost as:

owned_ptr_container<T> // destructor deletes elements
view_ptr_container<T> // destructor doesn't delete elements

Dave Moore
 
S

Siemel Naran

Dave Moore said:
Hmm .. aren't there ownership issues inherent to this idea? Who is to
say that the elements in the container belong to it? Couldn't the
pointer-elements have been initialized from already existing objects,
for example the elements of another container<T*>? It seems to me
that this would only really be useful in the context of the boost
smart-pointers, where ownership is already clear, IOW

Right, to make the intent clear we ought to name the container as
owned_pointer_container or something like that. But my intent was to not
use boost::shared_ptr in the implementation of my class.
shared_ptr_container<T> for container<shared_ptr<T> >, when the
elements will be owned by the container and
weak_ptr_container<T> for container<weak_ptr<T> > when they will not.

I suppose you could do a similar thing without boost as:

owned_ptr_container<T> // destructor deletes elements
view_ptr_container<T> // destructor doesn't delete elements

Yes, those are good names.
 
M

MikeB

owned_pointer_container or something like that. But my intent was to not
use boost::shared_ptr in the implementation of my class.

I'm a bit lost here. In your first reply to the OP, you suggested that he

"...could try smart pointers, like so:
std::vector<boost::shared_ptr<X> > d_container;"

I assumed that in doing so you were actually recommending that he *should*
try boost::shared_ptr.

Rgds,
MikeB
 
S

Siemel Naran

MikeB said:
I'm a bit lost here. In your first reply to the OP, you suggested that he

"...could try smart pointers, like so:
std::vector<boost::shared_ptr<X> > d_container;"

I assumed that in doing so you were actually recommending that he *should*
try boost::shared_ptr.

I gave 2 possibilities, one vector<boost::shared_ptr<T>> and the other
essentially vector<T*>. The owned_pointer_container method is just
vector<T*> with an interface to delete the internal items.
 

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,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top