Storing const references to other objects in another object.

J

James Allsopp

Hi,
I have a list of item objects which I'm trying to find which of these
objects are close to each other. The data in these objects does not
change. I const::iterate though the vector these are stored in, and
where the condition is met, create a linker object in another class. I
don't want to recreate the classes, just store a reference to them.
The code from the linker is

class linker{
public:
linker( const item &spectra, const item & core, double
distance):spectra(spectra),core(core),distance(distance){};
const item & spectra;
const item & core;
double distance;
int toString() const;

};

but get the following errors:
query.cpp:44: error: non-static reference member ‘const item&
linker::spectra’, can't use default assignment operator
query.cpp:44: error: non-static reference member ‘const item&
linker::core’, can't use default assignment operator

How do I get round this? item is a very simple class, shown below.

Thanks,
James

class item{
public:
item(std::string l, std::string lon, std::string lat);
std::string label;
double glon;
double glat;
string print() const ;
};

item::item(std::string l, std::string lon, std::string lat)
{
label=l;
char *t;
glon=strtod(lon.c_str(),&t);
glat=strtod(lat.c_str(),&t);
}

string item::print() const
{
stringstream s;
s << label << "\t" << glon <<"\t" << glat << endl;
return s.str();
}
 
K

Kai-Uwe Bux

James said:
Hi,
I have a list of item objects which I'm trying to find which of these
objects are close to each other. The data in these objects does not
change. I const::iterate though the vector these are stored in, and
where the condition is met, create a linker object in another class. I
don't want to recreate the classes, just store a reference to them.
The code from the linker is

class linker{
public:
linker( const item &spectra, const item & core, double
distance):spectra(spectra),core(core),distance(distance){};
const item & spectra;
const item & core;
double distance;
int toString() const;

};

but get the following errors:
query.cpp:44: error: non-static reference member ?const item&
linker::spectra?, can't use default assignment operator
query.cpp:44: error: non-static reference member ?const item&
linker::core?, can't use default assignment operator

It appears that on line 44, you have an assignment between linker objects.
Since the linker class has reference members, that does not work:
references, once initialized, cannot be reseated.

Options to fix this include:

a) Use pointers in the linker class instead of references.

b) Rewrite the client code so that assignment of linker objects does not
happen. (This might be possible since linker is still copy-constructibe.
However, assignability is a precondition for using linker objects within
standard containers.)


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Leigh said:
It is possible to re-seat a member reference in a user provided assignment
operator by doing something similar to the following:

const foo& foo::eek:perator=( const foo& other)
{
if ( this != &other )
{
this->~foo(); // lifetime of *this ends
new (this) foo(other); // new object of type foo created
}
return *this;
}

Yes, it is possible. However, there are some traps with that idea of which
one should be aware. E.g., that assignment operator does not really interact
nicely with inheritance in OO design (virtual members and such). E.g.:

#include <iostream>

struct X {

X ( void ) {
std::cout << "creating X\n";
}

virtual
~ X ( void ) {
std::cout << "destroying X\n";
}

X & operator= ( X const & other ) {
if ( this != &other ) {
// this->~X();
this->X::~X();
new (this) X (other);
}
return ( *this );
}

};

struct Y : public X {

Y ( void ) {
std::cout << "creating Y\n";
}

~ Y ( void ) {
std::cout << "destroying Y\n";
}

Y & operator= ( Y const & other ) {
X::eek:perator=( other );
return (*this );
}

};


int main ( void ) {
Y* a_ptr = new Y;
Y* b_ptr = new Y;
*a_ptr = *b_ptr;
delete a_ptr;
std::cout << "-----------\n";
Y* c_ptr = new Y;
delete c_ptr;
}

This has formally undefined behavior [3.8/7] and on my machine prints:

creating X
creating Y
creating X
creating Y
destroying X
destroying X
-----------
creating X
creating Y
destroying Y
destroying X



Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Leigh Johnston wrote:
[...]
It is possible to re-seat a member reference in a user provided assignment
operator by doing something similar to the following:

const foo& foo::eek:perator=( const foo& other)
{
if ( this != &other )
{
this->~foo(); // lifetime of *this ends
new (this) foo(other); // new object of type foo created
}
return *this;
}

Actually, I re-read [3.8/7]; and now I find that the above is not solving
the problem of reseating references. [3.8/7] states:

If, after the lifetime of an object has ended and before the storage which
the object occupied is reused or released, a new object is created at the
storage location which the original object occupied, a pointer that pointed
to the original object, a reference that referred to the original object, or
the name of the original object will automatically refer to the new object
and, once the lifetime of the new object has started, can be used to
manipulate the new object, if:
....
? the type of the original object is not const-qualified, and, if a class
type, does not contain any non-static data member whose type is const-
qualified or a reference type, and
....

The operational words are "or a reference type".


BTW: this is also in n3035.


Best

Kai-Uwe Bux
 
K

Keith H Duggar

Leigh Johnston said:
Leigh Johnston said:
Leigh Johnston wrote:
[...]
It is possible to re-seat a member reference in a user provided
assignment
operator by doing something similar to the following:
const foo& foo::eek:perator=( const foo& other)
{
if ( this != &other )
{
this->~foo(); // lifetime of *this ends
new (this) foo(other); // new object of type foo created
}
return *this;
}
Actually, I re-read [3.8/7]; and now I find that the above is not
solving
the problem of reseating references. [3.8/7] states:
If, after the lifetime of an object has ended and before the storage
which
the object occupied is reused or released, a new object is created at
the
storage location which the original object occupied, a pointer that
pointed
to the original object, a reference that referred to the original
object, or
the name of the original object will automatically refer to the new
object
and, once the lifetime of the new object has started, can be used to
manipulate the new object, if:
...
? the type of the original object is not const-qualified, and, if a
class
type, does not contain any non-static data member whose type is const-
qualified or a reference type, and
...
The operational words are "or a reference type".
BTW: this is also in n3035.
Best
Kai-Uwe Bux
Ah well spotted I failed again, damn UB. :)
For the record I have never done such a thing in real code. :)

And I would never have considered trying or recommending such a thing if it
wasn't for that damn example in 3.8/7 which I agree is bad practice (too
easy to invoke UB by mistake).

Exactly. Hence the common recommendation to use pointers as
members instead and be done with it. Do you still disagree
with this general advice and hold that "it is perfectly fine
to have reference member variables"? If so I'm starting to
wonder if we mean different things by the word "fine".

KHD
 
P

Puppet_Sock

It is possible to re-seat a member reference in a user provided assignment
operator by doing something similar to the following:

const foo& foo::eek:perator=( const foo& other)
{
  if ( this != &other )
  {
    this->~foo(); // lifetime of *this ends
    new (this) foo(other); // new object of type foo created
  }
  return *this;

}

Hmmm... I'm pretty sure I'd red-flag that in a code review.
Reseating a reference seems to me to be a sign that you
didn't want a reference in the first place.
Socks
 
J

Jonathan Lee

I tend to prefer references to enforce ownership relationships
as a reference cannot outlive what it refers to

That's not true in general. Using your example

foo::bar* dummy = 0;
do {
foo my_foo;
dummy = new foo::bar(my_foo, state_value);
} while (false);

// here dummy->iParent has outlived my_foo

Unless I've misunderstood you.

--Jonathan
 
A

Alf P. Steinbach

* Daniel T.:
So it proves my assertion that references were designed for argument
passing.

As I recall references were designed for operator overloading, in particular
supporting operator[], in order to make it possible to define types that for
most practical purposes act like if they were built-in, e.g. like arrays.

It's nice that references also turned out to be very useful for argument
passing, and for introducing short local names for things.

The one single baffling property of references is the "extend lifetime of
temporary".

In a discussion of this Bjarne once replied that it was mostly for uniformity,
no special rules.

I'm guessing that more information about the rationale for references is
available in his "... Design and Evolution" book.


Cheers & hth.,

- Alf
 
K

Keith H Duggar

All copied, no trimming.

Seriously Leigh, sometimes you have interesting
things to say. But you make it some hard to
find those interesting things that it's almost
not worth the effort.

Such usenet masterbation (replying to yourself often
and repeatedly) is just a symptom of his ADHD. Let's
hope he starts on those meds (or at the very least a
daily meditation or two) soon.

KHD
 
K

Keith H Duggar

You really are an asshole aren't you?

Yes, Little Teapot, Obsidian is black and cuts precisely.
You reply to yourself if you forget to mention something
in your original reply.

You forget because you are a brash, impatient, vociferous
wisehole. (At least you have been acting as such lately.)
I agree I should trim more though.

Then you refuse to accept the more important lesson which
is that you need to take an ADHD chill-pill and get ahold
of yourself.

KHD
 
Ö

Öö Tiib

And it has already been pointed out how limited such an idiom is and its
limitations stem directly from the fact that references were designed
for argument passing.

Possibility to limit something is useful feature. Your logic leaves
impression that language features limiting something (like private,
protected, const and volatile) are pointless since you get something
that is 'oh so limited' as result. ;)
 
G

gwowen

As the struggling comedian you seem to be you should realize that that joke
is wearing a bit thin and it is time to get new material.

Physician, heal thyself.
 

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,769
Messages
2,569,582
Members
45,062
Latest member
OrderKetozenseACV

Latest Threads

Top