user-defined op= for type with reference member

P

Paul Bibbings

Since I have used the following in another post, can someone just
confirm (or otherwise) whether the following definition of a
user-defined op= for a type with a reference member is well defined?

class AType
{
public:
AType(int& i)
: i_(i)
{ }
// ...
AType& operator=(const AType& other)
{
if (this != &other)
{
this->~Atype();
new (this) AType(other);
}
return *this;
}
private:
int& i_;
};

According to my reading of the example given in [basic.life] §3.8/7 I
believe that it is, in this instance (since the constructor doesn't
throw, except on bad_alloc).

Regards

Paul Bibbings
 
P

Paul Bibbings

Leigh Johnston said:
Paul Bibbings said:
Since I have used the following in another post, can someone just
confirm (or otherwise) whether the following definition of a
user-defined op= for a type with a reference member is well defined?

class AType
{
public:
AType(int& i)
: i_(i)
{ }
// ...
AType& operator=(const AType& other)
{
if (this != &other)
{
this->~Atype();
new (this) AType(other);
}
return *this;
}
private:
int& i_;
};

According to my reading of the example given in [basic.life] §3.8/7 I
believe that it is, in this instance (since the constructor doesn't
throw, except on bad_alloc).

Regards

Paul Bibbings

Attempting to use this trick to reseat a reference or
destruct/re-construct const members is UB IIRC, there was a thread
about this a few months ago I think.

I had that same recollection, but take a look at §3.8/7, where the
following example is given:

struct C {
int i;
void f();
const C& operator=( const C& );
};

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

C c1;
C c2;
c1 = c2; // well-defined
c1.f(); //well-defined; c1 refers to a new object of type C

The only relevant difference here is the reference data member, but I
have not been able to find a reason why this should present an especial
problem. The copy constructor can seat a reference, so I can't see why
using it in the context of a placement new (if that is the right term)
should be problematic. I will try and find the earlier thread.

Regards

Paul Bibbings
 
P

Paul Bibbings

Leigh Johnston said:
Pete Becker said:
Since I have used the following in another post, can someone just
confirm (or otherwise) whether the following definition of a
user-defined op= for a type with a reference member is well defined?

class AType
{
public:
AType(int& i)
: i_(i)
{ }
// ...
AType& operator=(const AType& other)
{
if (this != &other)
{
this->~Atype();
new (this) AType(other);
}
return *this;
}
private:
int& i_;
};

According to my reading of the example given in [basic.life] §3.8/7 I
believe that it is, in this instance (since the constructor doesn't
throw, except on bad_alloc).

Yes, it's well-defined, but it's a really bad idea:

class BType : public AType
{
public:
BType& operator=(const BType& other)
{
return AType::eek:perator==(other);
}
};

BType b1, b2;
b2 = b1; // nasty

In this particular example, there's almost certainly no nasty
behavior. But add a virtual function to AType (and make AType's
destructor virtual) and override the function in BType.

AType *at = &b2;
at->virtual_function();

Now things are messy, because the code says that b2 has type BType,
but BType's constructor has not been called.

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

It is well defined in the sense that it is defined to be undefined
behaviour. You cannot reseat references.

But you are not reseating a reference, surely. The above code -
notwithstanding Pete's comments which I am still digesting - destructs a
constructed object and creates a new one in its place. That is a very
different beast, wouldn't you say?

Regards

Paul Bibbings
 
K

Kai-Uwe Bux

Paul said:
Since I have used the following in another post, can someone just
confirm (or otherwise) whether the following definition of a
user-defined op= for a type with a reference member is well defined?

class AType
{
public:
AType(int& i)
: i_(i)
{ }
// ...
AType& operator=(const AType& other)
{
if (this != &other)
{
this->~Atype();
new (this) AType(other);
}
return *this;
}
private:
int& i_;
};

According to my reading of the example given in [basic.life] §3.8/7 I
believe that it is, in this instance (since the constructor doesn't
throw, except on bad_alloc).

It is undefined behavior according to [3.8/7, item 3], which requires:

...
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,
...

Since AType contains a non-static reference member, the trick does not work
for AType. Note that in the example in the standard, there is no non-static
reference member.


I know: it's a bummer since classes with reference members are about the
only cases, where one might even consider this trickery (which is poor form
anyway).


Best

Kai-Uwe Bux
 
F

Francesco S. Carta

Leigh Johnston said:
On 2010-06-11 11:21:09 -1000, Paul Bibbings said:
Since I have used the following in another post, can someone just
confirm (or otherwise) whether the following definition of a
user-defined op= for a type with a reference member is well defined?
   class AType
   {
   public:
      AType(int& i)
         : i_(i)
      { }
      // ...
      AType& operator=(const AType& other)
      {
         if (this != &other)
         {
            this->~Atype();
            new (this) AType(other);
         }
         return *this;
      }
   private:
      int& i_;
   };
According to my reading of the example given in [basic.life] 3.8/7 I
believe that it is, in this instance (since the constructor doesn't
throw, except on bad_alloc).
Yes, it's well-defined, but it's a really bad idea:
class BType : public AType
{
public:
BType& operator=(const BType& other)
{
return AType::eek:perator==(other);
}
};
BType b1, b2;
b2 = b1; // nasty
In this particular example, there's almost certainly no nasty behavior.
But add a virtual function to AType (and make AType's destructor virtual)
and override the function in BType.
AType *at = &b2;
at->virtual_function();
Now things are messy, because the code says that b2 has type BType, but
BType's constructor has not been called.
--
 Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

It is well defined in the sense that it is defined to be undefined
behaviour.  You cannot reseat references.

/Leigh


I'm not so sure whether the following could come interesting in this
case (nothing special for the most experienced ones, I suppose): it is
possible to store the reference as a pointer and create a "resettable
reference" in either of the following ways:

-------
#include <iostream>

using namespace std;

class IntByRef {
public:
explicit IntByRef(int& i) : ptr(&i) {};
operator int&() {
return *ptr;
}
int& operator=(int& i) {
ptr= &i;
return *ptr;
}
private:
int* ptr;
};

int main()
{

int local = 8;

IntByRef ibr(local);

cout << ibr << endl; // prints 8

local = 10;

cout << ibr << endl; // prints 10

int local2 = 42;

ibr = local2;

cout << ibr << endl; // prints 42

int& intref = ibr;

++ibr;
ibr *= 2;
ibr -= 8;

cout << intref << endl; // prints 78;

return 0;
}
-------


-------
#include <iostream>

using namespace std;

class SimplerIntByRef {
public:
SimplerIntByRef (int& i) : ptr(&i) {};
operator int&() {
return *ptr;
}
private:
int* ptr;
};

int main()
{

int local = 8;

SimplerIntByRef ibr(local);

cout << ibr << endl; // prints 8

local = 10;

cout << ibr << endl; // prints 10

int local2 = 42;

ibr = local2;

cout << ibr << endl; // prints 42

int& intref = ibr;

++ibr;
ibr *= 2;
ibr -= 8;

cout << intref << endl; // prints 78;

return 0;
}
-------

main() is practically identical for both cases, you can apply all
combined operator+equals to an IntByRef as well as all pre and post
increment/decrement, the only thing that you can't assign to it is a
constant or a temporary (that would be a plain rvalue) - by the means
of using operator= or by using the implicit ctor in the simpler case.
You can only (re)assign it to "point" to another (non-const) variable.

Modulo mistakes and misunderstandings as usual, I'm ready to have my
"view" fixed :)
 
P

Paul Bibbings

Kai-Uwe Bux said:
Paul said:
Since I have used the following in another post, can someone just
confirm (or otherwise) whether the following definition of a
user-defined op= for a type with a reference member is well defined?

class AType
{
public:
AType(int& i)
: i_(i)
{ }
// ...
AType& operator=(const AType& other)
{
if (this != &other)
{
this->~Atype();
new (this) AType(other);
}
return *this;
}
private:
int& i_;
};

According to my reading of the example given in [basic.life] §3.8/7 I
believe that it is, in this instance (since the constructor doesn't
throw, except on bad_alloc).

It is undefined behavior according to [3.8/7, item 3], which requires:

...
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,
...

Since AType contains a non-static reference member, the trick does not work
for AType. Note that in the example in the standard, there is no non-static
reference member.


I know: it's a bummer since classes with reference members are about the
only cases, where one might even consider this trickery (which is poor form
anyway).

It is indeed a bummer. I had somehow managed to `parse' 3.8/7
incorrectly and thought that I could escape it which, of course, I
can't. But that leaves me thinking, how /do/ you write a
copy-assignment operator for such a class with meaningful semantics?

Regards

Paul Bibbings
 
K

Kai-Uwe Bux

Paul Bibbings wrote:

[...]
But that leaves me thinking, how /do/ you write a
copy-assignment operator for such a class with meaningful semantics?

I'd say, you don't. If the class shall have an assignment operator, then I
would make sure that it has only pointer members (or better, a little smart
pointer like thingy that is closer to a reference in that it cannot be a
null pointer).


Best

Kai-Uwe Bux
 
M

Marcel Müller

Leigh said:
From 3.8/7:

"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"

Well, the question is who holds a pointer (or reference) to this at the
time of the reconstruction. If nobody does it, I cannot see any UB.
However, I can not imagine a use case of an assignment, without an
active reference to an assigned class.

But other design patterns with in place reconstruction may still be
defined behavior. Think about a factory that reuses memory of class
instances that are orphaned for some reason, and this is not detected
before the factory call. E.g. a connection pool, where the connection
parameters must match for the instances to be reused. Of course, no
assignment operator so far.
But one could implement this by an assignment operator as long as any
further access to the newly constructed instance is done through the
return value of the assignment operator. The compiler cannot assume that
this return value is *this and so it is just a new class instance that
happens to use the same storage. Of course, I would call this /very bad/
design, because the assignment operator behaves unexpected in the way
that it destroys *this. Any reuse of storage should be done /outside/
the class instance that occupies the storage. This reminds me of
discussions about 'delete this;'.


Marcel
 
M

Marcel Müller

Leigh said:
It is UB to reseat a reference. You are attempting to reseat a
reference if you call destructor and placement new in a class's
assignment operator i.e. "after the lifetime of an object has ended and
before the storage which the object occupied is reused or released".
Read the above section from the standard again. Violating a requirement
in the standard is UB and arguing this point is pointless.

The point is that I do not talk about the /same/ object. And if we do
not talk about the same object, this restriction of the standard does
not apply. It is only a new object which happens to use the same
storage. There is nothing wrong so far as long as no one relies on that.


Marcel
 
P

Paul Bibbings

It is UB to reseat a reference.  You are attempting to reseat a reference if
you call destructor and placement new in a class's assignment operator i.e.
"after the lifetime of an object has ended and before the storage which the
object occupied is reused or  released". Read the above section from the
standard again.  Violating a requirement in the standard is UB and arguing
this point is pointless.

/Leigh

I don't disagree, on the whole, with the conclusions reached in this
thread, but I am having some problem with it being stated in terms of
it
being "UB to reseat a reference." To my mind, "if you call destructor
and placement new in a class's assignment operator" you are *not*
thereby "reseat[ing] a reference." You *are* destructing an object
and
creating a new one in its place, with the attendant UB that has been
identified in the examples given; however, there is, to my mind, no
reference-reseating simply because, with the destruction of the
original
object, it's reference-member is destroyed with it and a *new* one
created in its place.

Now, if you consider the following:

#include <new>

class HasRefMem {
public:
HasRefMem(int& i)
: i_(i)
{ }
HasRefMem& operator=(const HasRefMem& other)
{
if (this != &other)
{
this->~HasRefMem();
new (this) HasRefMem(other);
}
return *this;
}
operator int&() { return i_; }
private:
int& i_;
};

int main()
{
int i, j;
HasRefMem hrmi(i);
HasRefMem hrmj(j);
int& i_ref = hrmi; // #1
hrmi = hrmj; // #2
}

then I might be able to consider i_ref (in line #1) as having been
`reseated' in line #2.

Regards

Paul Bibbings
 
P

Paul Bibbings

It is UB to reseat a reference.  You are attempting to reseat a reference if
you call destructor and placement new in a class's assignment operator i.e.
"after the lifetime of an object has ended and before the storage which the
object occupied is reused or  released". Read the above section from the
standard again.  Violating a requirement in the standard is UB and arguing
this point is pointless.

I don't disagree, on the whole, with the conclusions reached in this
thread, but I am having some problem with it being stated in terms of
it
being "UB to reseat a reference."  To my mind, "if you call destructor
and placement new in a class's assignment operator" you are *not*
thereby "reseat[ing] a reference."  You *are* destructing an object
and
creating a new one in its place, with the attendant UB that has been
identified in the examples given;  however, there is, to my mind, no
reference-reseating simply because, with the destruction of the
original
object, it's reference-member is destroyed with it and a *new* one
created in its place.

Now, if you consider the following:

   #include <new>

   class HasRefMem {
   public:
      HasRefMem(int& i)
         : i_(i)
      { }
      HasRefMem& operator=(const HasRefMem& other)
      {
         if (this != &other)
         {
            this->~HasRefMem();
            new (this) HasRefMem(other);
         }
         return *this;
      }
      operator int&() { return i_; }
   private:
      int& i_;
   };

   int main()
   {
      int i, j;
      HasRefMem hrmi(i);
      HasRefMem hrmj(j);
      int& i_ref = hrmi;       // #1
      hrmi = hrmj;             // #2
   }

then I might be able to consider i_ref (in line #1) as having been
`reseated' in line #2.

Well, actually ... no (replying to myself). i_ref will continue to
refer to i.

Regards

Paul Bibbings
 
K

Keith H Duggar

The point is that I do not talk about the /same/ object. And if we do
not talk about the same object, this restriction of the standard does
not apply. It is only a new object which happens to use the same
storage. There is nothing wrong so far as long as no one relies on that.

Please read the section of the standard again more carefully.
It does not refer to "the same object". It specifically refers
to the following noun phrases:

"an object"
"the storage which the [an] object occupied"
"a new object"
"the storage location which the original [an] object occupied"

ie it discusses the /storage/ that the /original/ and /new/
objects occupy. And it explicitly distinguishes between two
different objects: /original/ and /new/. In other words,
there is no "/same/ object" requirement as you thought.
Rather there is a same /storage/ requirement.

3.8/7 :
"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, ..."

KHD
 
B

Bo Persson

Keith said:
The point is that I do not talk about the /same/ object. And if we
do not talk about the same object, this restriction of the
standard does not apply. It is only a new object which happens to
use the same storage. There is nothing wrong so far as long as no
one relies on that.

Please read the section of the standard again more carefully.
It does not refer to "the same object". It specifically refers
to the following noun phrases:

"an object"
"the storage which the [an] object occupied"
"a new object"
"the storage location which the original [an] object occupied"

ie it discusses the /storage/ that the /original/ and /new/
objects occupy. And it explicitly distinguishes between two
different objects: /original/ and /new/. In other words,
there is no "/same/ object" requirement as you thought.
Rather there is a same /storage/ requirement.

How is it not the same object after an assignment? That doesn't create
a new object, does it?


Bo Persson
 
K

Keith H Duggar

Please read the section of the standard again more carefully.
It does not refer to "the same object". It specifically refers
to the following noun phrases:
"an object"
"the storage which the [an] object occupied"
"a new object"
"the storage location which the original [an] object occupied"
ie it discusses the /storage/ that the /original/ and /new/
objects occupy. And it explicitly distinguishes between two
different objects: /original/ and /new/. In other words,
there is no "/same/ object" requirement as you thought.
Rather there is a same /storage/ requirement.

How is it not the same object after an assignment? That
doesn't create a new object, does it?

It does when a destructor + constructor sequence is called in
the assignment (the case being discussed). Below is a relevant
portion from the standard regarding object lifetime:

3.8 Object Lifetime
[basic.life]

1 The lifetime of an object is a runtime property of the object.
The
lifetime of an object of type T begins when:

--storage with the proper alignment and size for type T is
obtained,
and

--if T is a class type with a non-trivial constructor
(_class.ctor_),
the constructor call has completed.

The lifetime of an object of type T ends when:

--if T is a class type with a non-trivial destructor
(_class.dtor_),
_________________________
9) On some implementations, it causes a system-generated
runtime
fault.

the destructor call starts, or

--the storage which the object occupies is reused or released.

KHD
 
B

Bo Persson

Keith said:
Keith said:
On Jun 13, 9:40 am, Marcel M?¬?ller <[email protected]>
wrote:
Leigh Johnston wrote:
It is UB to reseat a reference. You are attempting to reseat a
reference if you call destructor and placement new in a class's
assignment operator i.e. "after the lifetime of an object has
ended and before the storage which the object occupied is reused
or released". Read the above section from the standard again.
Violating a requirement in the standard is UB and arguing this
point is pointless.
The point is that I do not talk about the /same/ object. And if
we do not talk about the same object, this restriction of the
standard does not apply. It is only a new object which happens to
use the same storage. There is nothing wrong so far as long as no
one relies on that.
Please read the section of the standard again more carefully.
It does not refer to "the same object". It specifically refers
to the following noun phrases:
"an object"
"the storage which the [an] object occupied"
"a new object"
"the storage location which the original [an] object occupied"
ie it discusses the /storage/ that the /original/ and /new/
objects occupy. And it explicitly distinguishes between two
different objects: /original/ and /new/. In other words,
there is no "/same/ object" requirement as you thought.
Rather there is a same /storage/ requirement.

How is it not the same object after an assignment? That
doesn't create a new object, does it?

It does when a destructor + constructor sequence is called in
the assignment (the case being discussed). Below is a relevant
portion from the standard regarding object lifetime:

3.8 Object Lifetime
[basic.life]

1 The lifetime of an object is a runtime property of the object.
The
lifetime of an object of type T begins when:

--storage with the proper alignment and size for type T is
obtained,
and

--if T is a class type with a non-trivial constructor
(_class.ctor_),
the constructor call has completed.

The lifetime of an object of type T ends when:

--if T is a class type with a non-trivial destructor
(_class.dtor_),
_________________________
9) On some implementations, it causes a system-generated
runtime
fault.

the destructor call starts, or

--the storage which the object occupies is reused or released.

KHD

My point was rather the same as Leigh(?) above, that an assignment
doesn't create a new object so the reuse rule doesn't apply.

In the code

SomeObject x, y;

x = y;

it is the same object x both before and after the assignment. This is
not a reuse of storage.


Bo Persson
 
P

Paul Bibbings

Bo Persson said:
Keith said:
How is it not the same object after an assignment? That
doesn't create a new object, does it?

It does when a destructor + constructor sequence is called in
the assignment (the case being discussed). Below is a relevant
portion from the standard regarding object lifetime:

3.8 Object Lifetime
[basic.life]

1 The lifetime of an object is a runtime property of the object.
The
lifetime of an object of type T begins when:

--storage with the proper alignment and size for type T is
obtained,
and

--if T is a class type with a non-trivial constructor
(_class.ctor_),
the constructor call has completed.

The lifetime of an object of type T ends when:

--if T is a class type with a non-trivial destructor
(_class.dtor_),
_________________________
9) On some implementations, it causes a system-generated
runtime
fault.

the destructor call starts, or

--the storage which the object occupies is reused or released.

KHD

My point was rather the same as Leigh(?) above, that an assignment
doesn't create a new object so the reuse rule doesn't apply.

In the code

SomeObject x, y;

x = y;

it is the same object x both before and after the assignment. This is
not a reuse of storage.

Remember that, in the example code originally given, the only issue that
resulted in undefined behaviour was the fact that the class for which
the op= was being defined contained a reference member. If we put that
aside for a moment we get, effectively, the example given in
[basic.life] §3.8/7, which I give below, complete with (unchanged)
comments from the text:

struct C {
int i;
void f();
const C& operator=( const C& );
};

const C& C::eek:perator=( const C& other)
{
if ( this != &other ) {
this->ËœC(); // lifetime of *this ends
new (this) C(other); // new object of type C created
f(); // well-defined
}
return *this;
}

C c1;
C c2;
c1 = c2; // well-defined
c1.f(); // well-defined; c1 refers to a new object of type C

You will see clearly the succession "lifetime of *this ends," "new
object of type C created" and, in the last line "c1 refers to a *new*
object of type C."

All this *because* of the particular way in which the assignment has
been implemented.

Regards

Paul Bibbings
 
J

James Kanze

Since I have used the following in another post, can someone
just confirm (or otherwise) whether the following definition
of a user-defined op= for a type with a reference member is
well defined?
class AType
{
public:
AType(int& i)
: i_(i)
{ }
// ...
AType& operator=(const AType& other)
{
if (this != &other)
{
this->~Atype();
new (this) AType(other);
}
return *this;
}
private:
int& i_;
};
According to my reading of the example given in [basic.life]
§3.8/7 I believe that it is, in this instance (since the
constructor doesn't throw, except on bad_alloc).
Attempting to use this trick to reseat a reference or
destruct/re-construct const members is UB IIRC, there was a
thread about this a few months ago I think.

It's not undefined behavior per se. The standard is quite
clear that it is defined, and what its semantics are.

The problem is that its defined semantics can lead to undefined
behavior in so many cases that it's just not worth the bother;
the risk is too great. (And the undefined behavior can appear
as a result of some future evolution in the code.)

With regards to the orginal poster's problem: the answer is
simple: if you want to support assignment, use a pointer, rather
than a reference.
 
J

James Kanze

Paul Bibbings wrote:

[...]
It is undefined behavior according to [3.8/7, item 3], which requires:
...
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,
...

I wonder if this text was present in the original version of the
standard. I seem to recall this being legal. (I know that
there was some discussion of it in the committee at the time.)
Since AType contains a non-static reference member, the trick
does not work for AType. Note that in the example in the
standard, there is no non-static reference member.
I know: it's a bummer since classes with reference members are
about the only cases, where one might even consider this
trickery (which is poor form anyway).

The real motivation for the idiom is handling assignment of
virtual base classes correctly. The real problem is that the
idiom doesn't work when inheritance is involved, which makes it
pretty useless for the only real motivation. And of course,
since assignment and inheritance don't work well together
anyway, the fact that assignment is particularly difficult to
get right when virtual base classes are involved isn't really an
issue.
 
F

Francesco S. Carta

Paul Bibbings said:
Personally, I cannot see what the point would be of class modelling a reference,
albeit a reseatable one, that didn't permit the simple assignment:

Actually, I didn't speculate that much about the uses of my class. We
could eventually consider it something like a read-only, only that it
isn't... read on...
int i = 1;
IntByRef ibr(i);
ibr = 2;

Yes, that last line would be forbidden with my class and the compiler
would complain, as if it were read-only - only that the simple
assignment with an actual lvalue would reseat the reference, and that
would make it not read-only - not look like a read-only, that would be
misleading... read on...
For myself, I would have the assignment operators assign *values*, and have the
reseating required to be done explicitly. Something like:

template<typename T>
class ReseatableRef
{
ReseatableRef(T& t): t_ptr(&t) { }
ReseatableRef& operator=(const T& t) // value assignment
{
*t_ptr = t;
return *this;
}
ReseatableRef& operator=(const ReseatableRef& other) // ditto!!!
{
*t_ptr = *other.t_ptr;
return *this;
}
ReseatableRef& reseat(T& t)
{
t_ptr =&t;
return *this;
}
operator T&() { return *t_ptr; }
operator T() const { return *t_ptr; }
T * operator&() const { return t_ptr; }
private:
T *t_ptr;
};

int main()
{
int local = 8;
iref_t ibr(local);
std::cout<< "1: ibr = "<< ibr<< '\n';

local = 10;
std::cout<< "2: ibr = "<< ibr<< '\n';

ibr = 42; // !!!
std::cout<< "3: ibr = "<< ibr<< '\n';

local = 0;
int local2 = 1;
ibr.reseat(local2);
std::cout<< "4: ibr = "<< ibr<< '\n';

int& local22 = local2;
int& local222 = local22;

std::cout<< "&local2 = "<< &local2<< '\n';
std::cout<< "&local22 = "<< &local22<< '\n';
std::cout<< "&local222 = "<< &local222<< '\n';

iref_t ibr2(ibr);
iref_t ibr3(ibr2);

std::cout<< "&ibr = "<< &ibr<< '\n';
std::cout<< "&ibr2 = "<< &ibr2<< '\n';
std::cout<< "&ibr3 = "<< &ibr3<< '\n';
}

/**
* Output:
* 1: ibr = 8
* 2: ibr = 10
* 3: ibr = 42
* 4: ibr = 1
*&local2 = 0x22cd08
*&local22 = 0x22cd08
*&local222 = 0x22cd08
*&ibr = 0x22cd08
*&ibr2 = 0x22cd08
*&ibr3 = 0x22cd08
*/

I think that this would be closer to the behaviour of an ordinary reference over
all, with the *addition* that it can be reseated. Of course, it also allows for
the creation of a `reference to a reference':)

Definitely better, thanks for pointing that out.

It's not that rare to see me posting code out of the top of my head,
just for the sake of explaining my point about something - luckily, some
kindhearted ones happen to pass and fix, extend or somehow else modify
my code and make it something (more) usable :)
 

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,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top