Classes with reference members in a vector.

S

sidney

Hi all,

I am trying to make a vector containing objects the have a reference
member. However, as soon as I try to push_back an element into this
vector, g++ balks at the fact that it needs to instantiate an operator=
on the class containing the reference members (see below for what I try
to do).

#include <vector>

class TestClass {
private:
const int & m_q;
public:
TestClass(const int & q) { m_q = q; }
};

int main()
{
std::vector<TestClass> v;
int hello = 5;
v.push_back(TestClass(hello));
}

I would have expected that a copy constructor call would be executed in
the offended line instead of an assignment.

Can anyone explain how the desired operation could be implemented (or
why it doesn't make sense for me to want it to work?).

Best regards, Sidney
 
S

sidney

Oops. In the previous mail, please read ...

TestClass(const int & q): m_q(q) { }

.... for the constructor definition. It should be immediately
initialized, of course.... But the problem remains.

Cheerio,

Sidney
 
V

Vikram

Oops. In the previous mail, please read ...

TestClass(const int & q): m_q(q) { }

... for the constructor definition. It should be immediately
initialized, of course.... But the problem remains.

Cheerio,

Sidney

I dont think you can have references inside a STL container (like
Vector). Mainly because of issues while copying. A reference, unlike a
pointer, can only be set once.

HTH,
Vikram
 
S

sidney

Vikram said:
I dont think you can have references inside a STL container (like
Vector). Mainly because of issues while copying. A reference, unlike a
pointer, can only be set once.

That could well be an STL design decision or limitation that I am not
aware of. Note, however, that at no point I want to set a reference
more than once.
 
?

=?iso-8859-1?q?Kirit_S=E6lensminde?=

This is not true. A reference can be set many times unless it is const.
You must however assign a value to it when it is constructed.
That could well be an STL design decision or limitation that I am not
aware of. Note, however, that at no point I want to set a reference
more than once.

In order to grow the vector often a new one is needed. The code then
allocates the new vector and copies the old members into it - this will
use an assignment.

Depending on the implementation and how you use it in the rest of your
code you may be able to get away with a std::list. To get some proper
advice though we'd need to know a lot more about what you want to
achieve.


K
 
V

Vikram

Kirit said:
This is not true. A reference can be set many times unless it is const.
You must however assign a value to it when it is constructed.


Sorry..but how? I am not aware of any way to re-assign a reference
variable. Infact, Scott Meyer's "More effective C++" has that as the
1st item.
Maybe I am missing something here

--Vikram
 
F

Frederick Gotham

Vikram posted:
Sorry..but how? I am not aware of any way to re-assign a reference
variable. Infact, Scott Meyer's "More effective C++" has that as the
1st item.
Maybe I am missing something here



I presume, (although I don't know if he meant it in a smart-alaky way),
that he mean that you can do:

int i;

int &r = i;

int a = 1, b = 2, c = 3, d = 4;

r = a; r = b; r = c; r = d;


However, any C++ programmer who's out of diapers will tell you that
you're setting the value of "i", NOT changing the object to which the
reference refers.

If you ever change the object to which a reference refers, I'll give you
my house, and also eat my hat for good measure.
 
S

sidney

Kirit said:
In order to grow the vector often a new one is needed. The code then
allocates the new vector and copies the old members into it - this will
use an assignment.

Why does it allocate (calling a constructor), then assign? To me it is
not inconceivable that one could gop around this, by having the
push_back() using the copy constructor for combined allacation and
assignment.
Depending on the implementation and how you use it in the rest of your
code you may be able to get away with a std::list.

I need random access via operator[], so a vector is needed.
To get some proper advice though we'd need to know a lot more about what you want to
achieve.

I have a Class that contains references to items that are managed by a
different class, i.e., the Class isn't responsible for their
deallocation, and it may assume that the referenced items exist during
its lifetime, furthermore, I don't need the ability to set them to NULL
- in fact, I would like the help of the compiler to make sure that
doesn't happen.

Currently I just use pointer members but IMHO it would be preferable to
turn the members into references because of these guarantees - the
referred members /are/ properly thought of as aliases of the objects
themselves, instead of pointers to them, making references the natural
choice.

So basically all would be swell if I could just put an instance of
these classes in a vector, which is a-priori not much to ask. It could
be a genuine limitation of the STL but I would be interested if there
were a deeper reason that what I want would be bad.

I don't see an a-priori reason why one /can/ have a class C with
reference members, but one /cannot/ have a usable std::vector<C>. This
issue is independent of my particular problem in my particular program,
it is more a generic question about understanding why this seems to be
a problem with STL or C++.
 
I

Ian Price

Take a look at the code for push_back (g++ 3.3) in
/usr/local/c++/3.3/bits/stl_vector.h.

If there is space to spare then the copy constructor is used to
append your new object (using _Construct, which amounts to a
placement new operation). However, if there isn't enough space
available
and the vector needs to reallocate storage, then _M_insert_aux is
called. This is the cause of the problem, and is more obscure (code in
vector.tcc)
because _m_insert_aux is also designed for inserting anywhere in the
vector,
not just at the end. For push_back, the code executed only needs to
use the
copy constructor (for both the copy of the existing objects and the
append of
your new object). Unfortunately, the alternate branch in the 'if' uses
the
assignment operator, and that is why the compiler complains...

In my opinion, the use of _M-insert_aux in push_back was a poor choice.
There
is no need for the runtime-evaluation of 'if', and it could be
implemented so
that operator= does not need to be instanciated by the compiler.
Perhaps other STL implementations are different and this is just a g++
issue.

Ian
 
?

=?iso-8859-1?q?Kirit_S=E6lensminde?=

Frederick said:
Vikram posted:



I presume, (although I don't know if he meant it in a smart-alaky way),
that he mean that you can do:

int i;

int &r = i;

int a = 1, b = 2, c = 3, d = 4;

r = a; r = b; r = c; r = d;


However, any C++ programmer who's out of diapers will tell you that
you're setting the value of "i", NOT changing the object to which the
reference refers.

If you ever change the object to which a reference refers, I'll give you
my house, and also eat my hat for good measure.

Your house and hat are obviously safe :) I just explained it very
badly in my haste to answer the second half.


K
 
F

Fredrick Gotham

Kirit Sælensminde posted:

Your house and hat are obviously safe :) I just explained it very
badly in my haste to answer the second half.


I also would have accepted:

Use std::swap!


: )
 
S

sidney

Perhaps other STL implementations are different and this is just a g++
issue.

Thanks Ian. If I hear what you're saying, there is no reason (technical
or philosophical) why the code originally provided shouldn't work.

The question then becomes whether the g++ behavior violates the STL
standard in any way....

Does the STL spec make any statements regarding which types of
operations may instantiate which types of operators, constructors, copy
constructors, and such?

If not, it is extremely hairy to write code that is 100% portable
across STL implementations. The programmer would have no way of knowing
if his code will port to a different implementation without trying -
there will be no guarantees.

If yes, I can go look it up and see if I violate any rules...

If not, I can file a bug report with the g++ people.

If yes I will just sob a bit :-/

Does anyone know?
 
S

sidney

Does anyone know?

Ahh ... ISO/IEC 14882, Clause 23.4 (which talks about the requirements
for objects to be stored in STL containers...)

#3 The type of objects stored in these components must meet the
requirements of 'CopyConstructible' types (20.1.3) and the additional
requirements of 'Assignable' types.

..... and the 'Assignable' types requirement, basically, requires a
working operator=().

So it looks like I'm screwed. Anything that you drop in any STL
container must be assignable. Which basically means that you cannot
portably put ANY classes with reference-type members inside /any/ STL
container.

That's rather a hefty restriction, I would say!

Cheerio,

Sidney
 
V

Vikram

Ahh ... ISO/IEC 14882, Clause 23.4 (which talks about the requirements
for objects to be stored in STL containers...)

#3 The type of objects stored in these components must meet the
requirements of 'CopyConstructible' types (20.1.3) and the additional
requirements of 'Assignable' types.

.... and the 'Assignable' types requirement, basically, requires a
working operator=().

So it looks like I'm screwed. Anything that you drop in any STL
container must be assignable. Which basically means that you cannot
portably put ANY classes with reference-type members inside /any/ STL
container.

That's rather a hefty restriction, I would say!

Cheerio,

Sidney

Well, it is based on an "Assignable Model" so I guess that is expected.
I think thats the least restriction that can be put especially if
algorithms like copy/sort are to be supported. Not to mention the
internal copying that goes on as someone else pointed out.
 
S

sidney

Vikram said:
Well, it is based on an "Assignable Model" so I guess that is expected.
I think thats the least restriction that can be put especially if
algorithms like copy/sort are to be supported. Not to mention the
internal copying that goes on as someone else pointed out.

I am not an STL expert by any stretch of the imagination, but I am
having a hard time seeing why Assignable is definitely needed on top of
CopyConstructible, - i.e., why the standard writers elected to do this.

I imagine that both copy() and sort() can be written in terms of
copy-constructors rather than operator=.

Anyway, I will have to deal with it. No reference members in classes
that go into STL containers.... It just makes some classes rather less
clean than they could be, for no apparent reason (to me at least). At
least when I would like to put them in STL containers.

Having to declare class members as pointers rather than references
because the STL containers are unable to handle them properly makes me
a bit uncomfortable I must say.
 
?

=?ISO-8859-15?Q?Juli=E1n?= Albo

I am not an STL expert by any stretch of the imagination, but I am
having a hard time seeing why Assignable is definitely needed on top of
CopyConstructible, - i.e., why the standard writers elected to do this.
I imagine that both copy() and sort() can be written in terms of
copy-constructors rather than operator=.

You can write your own container without that restriction. The standard
containers aims to be for general use, but not to solve all possible
problems in any conceivable circumstance.
 
H

Howard

I am not an STL expert by any stretch of the imagination, but I am
having a hard time seeing why Assignable is definitely needed on top of
CopyConstructible, - i.e., why the standard writers elected to do this.

I imagine that both copy() and sort() can be written in terms of
copy-constructors rather than operator=.

You _could_ write a sort so that every time you swapped items, you created
new ones using their copy constructors and then deleted the old ones. But
that would be pretty inefficient, wouldn't it? Especially if
construction/destruction were expensive for the given class. Assignment is
the usual way.
Anyway, I will have to deal with it. No reference members in classes
that go into STL containers.... It just makes some classes rather less
clean than they could be, for no apparent reason (to me at least). At
least when I would like to put them in STL containers.

You could always store pointers to your objects in the vector, instead of
the objects themselves. (It seems as if that's what I always end up doing
anyway, for one reason or another.)

-Howard
 
S

sidney

Julián Albo said:
You can write your own container without that restriction. The standard
containers aims to be for general use, but not to solve all possible
problems in any conceivable circumstance.

I don't think that what I want to do is particularly exotic, to be
honest. C++ has references which is nice. It has the STL, which is
nice. It's just a pity to conclude that they don't get along too well.
 
W

werasm

Vikram wrote:
I imagine that both copy() and sort() can be written in terms of
copy-constructors rather than operator=.

- Especially sorting requires swapping. This requires assignment.
- Copying, especially when having two established ranges, require
assignment, else explicitly calling destructor and inplace construction
would be required - what if inplace construction through exception?
Then the container would be left in an unusable state. Impossible to
write exception safe (value semantics) container if assignment not
allowed (my guess).

Anyway, I will have to deal with it. No reference members in classes
that go into STL containers.... It just makes some classes rather less
clean than they could be, for no apparent reason (to me at least). At
least when I would like to put them in STL containers.

You have many options - why not use pointers, or boost::shared_ptr - it
is copyable and assignable and can be used in containers.
Having to declare class members as pointers rather than references
because the STL containers are unable to handle them properly makes me
a bit uncomfortable I must say.

I would not use reference members in the first place. They give you a
false sense of security. If you want to share objects, use something
like boost::shared_ptr.

One last thing, if you really want to use references in your containers
(no reason to, apart from proving you can) - have a look at boost::ref
library. This is probably not its (initial) intent though.

Code would look something like this. I only checked that it compiled -
requires the boost library of course, but you could easily write your
own ref wrapper...

#include <boost/ref.hpp>
#include <vector>
#include <iostream>

int main()
{
typedef boost::reference_wrapper<int> irefWrapper;
typedef std::vector<irefWrapper> ref_vect;

int v1( 0 );
int v2( 1 );
int& r1( v1 );
int& r2( v2 );

ref_vect rvect;
rvect.push_back( irefWrapper( r1 ) );
rvect.push_back( irefWrapper( r2 ) );

std::copy( rvect.begin(), rvect.end(),
std::eek:stream_iterator<int>(std::cout, " Missipi \n") );
std::cin.get();
return 0;
}

Regards,

Werner
 
S

sidney

Howard said:
You _could_ write a sort so that every time you swapped items, you created
new ones using their copy constructors and then deleted the old ones.

A swap would be much preferable, you are right. Which makes another
interesting case - it will be impossible to swap two instances of
classes that have reference-type members.
that would be pretty inefficient, wouldn't it? Especially if
construction/destruction were expensive for the given class. Assignment is
the usual way.

I have a hard time thinking of scenarios where the combined default
constructor and operator= would outperform a well-written copy
constructor?
You could always store pointers to your objects in the vector, instead of
the objects themselves. (It seems as if that's what I always end up doing
anyway, for one reason or another.)

Still, a vector of /references/ would often come in handy I suppose.
And, in my particular app I'm trying to store pairs of references -
which would have been quite cheap. It's now just a vector of pointer
pairs.

Regards,

Sidney
 

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

No members online now.

Forum statistics

Threads
473,800
Messages
2,569,657
Members
45,408
Latest member
SandyR2016
Top