Topic: lvalue Derived to reference to Base - where in Std?


Author: sNOiSPAMt@amu.edu.pl ("S.Tobias")
Date: Thu, 22 Jul 2004 02:22:17 GMT
Raw View
johnchx <johnchx2@yahoo.com> wrote:
> sNOiSPAMt@amu.edu.pl ("S.Tobias") wrote

> The important thing to notice is that a reference binds *either* to an
> lvalue *or* to an object/sub-object, never both.  Which one depends
> upon whether the intializer expression is itself an lvalue or an
> rvalue.

This is something new to me; it means my previous understanding
was wrong.  From what I've been able to find in the Internet,
the idea of binding comes from beyond C++ language; maybe this is
the reason why the Standard does not try to define it.  Could you
please point me to some resources, preferably on-line, covering
the subject, with emphasis on C++ if possible?

>From the reading of your post and the Standard, I gather that when
a reference is bound to a (sub)object (or rvalue), the reference
provides for the lifetime of the (complete) object; whereas when a
reference is bound to an (lvalue) expression, it is merely pointed to
the object represented by the expression, and it's the programmer's
responsibility to make sure that the lifetime of the object does
not end before the lifetime of the reference.
Is that right?
IOW, the following piece is semantically erroneous:
  char pc = new char;
  char &rc = *pc;     //rc is bound to lvalue
  delete pc;     //error
(I think this is the same thing you tried to explain in your
example, but somewhat more explicit.  Also, I think it shows that
lvalue-to-rvalue conversion is not always possible, ie. when the
environment cannot guarantee the lifetime of an object; if we could
somehow magically convert *pc to an rvalue, rc would be bound to
an object pc points to, and delete pc would bring the disaster.)


> Returning to the reference-to-base question...a reference of type
> Base& initialized with an object of type Dervived (whether it binds to
> the object or not) refers to an object of type Derived.  Using the

I get confused here.  The Standard in the Example in 8.5.3/5 (the
one I gave in my original post) says "ra refers to A sub-object
in b"; therefore the reference refers to an (sub)object of type
Base (I understand "refers to" means "aliases", or "is pointed to"
- using pointer terms).

> reference constitutes accessing an object of type Derived through an
> lvalue of type Base.  This is permitted by 3.10/15.

Strictly, that paragraph says about accessing "the stored value of
an object";  I don't know if it is the same or not, eg. it might
not cover all kinds of object access (eg. B &rb = d;  ra = x;
here I access d through the reference rb, but not for the value).


> I'm not sure the semantics of aliasing in this fashion are explicitly
> spelled out in the standard, but implementors don't seem to be in
> urgent need of clarification.

If it's not in the Standard, then I'm very disappointed.  If it's not
there, then isn't use of references to base actually UB?  I'd be very
glad if someone proved it isn't, or sent a request for clarification
to the Committee.  I'll keep searching.  Thank you for your answer.

--
Stan Tobias
sed '/[A-Z]//g' to email

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: johnchx2@yahoo.com (johnchx)
Date: Tue, 6 Jul 2004 22:06:38 +0000 (UTC)
Raw View
sNOiSPAMt@amu.edu.pl ("S.Tobias") wrote

> > > 8.5.3/5 (References)
> > >   Description (wrong?).  In the Example class A is obviously
> > >   reference-compatible with class B, so - according to the Standard
> > >   - the reference is bound to the initializer lvalue, ie. `b';
> >
> > Absolutely.  This is the definition of the magic.  The standard says
> > it happens, believe it!
>
> I think the problem is that the Standard uses the word "bind" for
> references, but does not define it; and it uses it in different ways.
> Here (and elsewhere) the Standard says that reference is bound to an
> expression, but a few bullets further (and also in 12.2/5 (Temporary
> objects)) it says a reference is bound to a (sub)object.  I still don't
> think I understand what the Standard means there.

The important thing to notice is that a reference binds *either* to an
lvalue *or* to an object/sub-object, never both.  Which one depends
upon whether the intializer expression is itself an lvalue or an
rvalue.

Reference binding seems to be one of those terms which the standard
defines only by extension:  it tells us how and when binding takes
place, it tells us the effects of binding (in particular extending the
lifetime of a temporary object bound to a reference), but it doessn't
ever explain what binding *means* in an intuitive sense.

It would, I suspect, be difficult to formulate a satisfying intuitive
definition for a term that can apply equally to an object and to an
expression.  It's just plain unnatural....

It's also important to note that a reference always *refers* to an
object or function, regardless of what it *binds* to.

The distinction between binding to an object and binding to an
expression explains why the following doesn't work:

struct foo {
  foo& me() {return *this;}
  void bar();
};

foo getfoo();

int main () {
  foo& f = getfoo().me();
  f.bar();  // kaboom!
}

If the reference f were bound to the temporary object, the lifetime of
the temporary would be extended and the call to f.bar() would be
valid.  However, f actually binds to the lvalue expression
getfoo().me() (and not to any object at all), so the lifetime of the
temporary will end at the end of the full expression.

Returning to the reference-to-base question...a reference of type
Base& initialized with an object of type Dervived (whether it binds to
the object or not) refers to an object of type Derived.  Using the
reference constitutes accessing an object of type Derived through an
lvalue of type Base.  This is permitted by 3.10/15.

I'm not sure the semantics of aliasing in this fashion are explicitly
spelled out in the standard, but implementors don't seem to be in
urgent need of clarification.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Sat, 10 Jul 2004 21:27:50 +0000 (UTC)
Raw View
On Mon, 5 Jul 2004 15:41:38 +0000 (UTC), sNOiSPAMt@amu.edu.pl
("S.Tobias") wrote:

> John Potter <jpotter@falcon.lhup.edu> wrote:

> > It is bound to the expression which is an lvalue.  How the address of
>                                                          ^^^^^^^^^^^^^^
> > that bound reference turns out to be the address of the subobject of
>   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > b is implementation magic.

> That's exactly what I want!  Where does the Standard guarantee it?
> IOW:  What guarantees: &static_cast<Base&>(d) == static_cast<Base*>(&d) ?
> What the right-hand side value is (ie. what the pointer points to),
> is described in 4.10/3 (Pointer conversions); I can't find similar
> description for references in a proper place.

Yes.  You might note the derived-to-base conversion used in clause 13 is
explained to not really be a conversion.  It is treated as a conversion
for overload resolution only.

The whole point is that binding a reference to an lvalue does not
involve any conversions.  That is important because a conversion
produces a new object and reference binding does not.  8.5.3/5 is
the best we can do.  In the statement A& ra = b, the comment states
that ra refers to the A subobject of b and the normative text states
that ra is bound to the lvalue expression b.  Not a problem, ra is
bound to b and refers to the A subobject.

I hope that is better.  None of the language lawyers seem to want to
explain this.

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: sNOiSPAMt@amu.edu.pl ("S.Tobias")
Date: Mon, 5 Jul 2004 15:41:38 +0000 (UTC)
Raw View
John Potter <jpotter@falcon.lhup.edu> wrote:
> On Mon, 21 Jun 2004 16:57:44 +0000 (UTC), sNOiSPAMt@amu.edu.pl
> ("S.Tobias") wrote:
>
> > [Example from 8.5.3/5]
> >
> >   struct A { };
> >   struct B : public A { } b;
> >   A& ra = b;                     // ra refers to A sub-object in b
>
> > Where in the Standard is *described* conversion (or whatever)
> > of derived class lvalue to reference to its base?
>
> The problem may be that you are looking for something which does
> not exist.  References are not converted (or whatevered).  There
> is just some simple magic which happens with references.

When a pointer to Derived is converted to a pointer to Base its value
must be adjusted to point to the right sub-object.  Similar concept must
apply to references to Base - they must be glued to the right part of
an object.  I abused the word "conversion" because I didn't have any
better one for what happens when a reference to Base is initialized.



> > 5.2.7/5 (Dynamic cast)
> >   Description given for dynamic_cast ("lvalue for the unique B
> >   sub-object").  Not applicable here.
>
> Why is it not applicable?  Replacing the pointers with references
> requires no conversion due to magic.

I referred only to the part which says about references, which has a
good explanation.  This is not applicable, because this part is about
dynamic cast, and we're currently in reference initialization domain.



> > 8.5.3/5 (References)
> >   Description (wrong?).  In the Example class A is obviously
> >   reference-compatible with class B, so - according to the Standard
> >   - the reference is bound to the initializer lvalue, ie. `b';
>
> Absolutely.  This is the definition of the magic.  The standard says
> it happens, believe it!

I think the problem is that the Standard uses the word "bind" for
references, but does not define it; and it uses it in different ways.
Here (and elsewhere) the Standard says that reference is bound to an
expression, but a few bullets further (and also in 12.2/5 (Temporary
objects)) it says a reference is bound to a (sub)object.  I still don't
think I understand what the Standard means there.
(The Standard also says that objects are bound to references; I assume
that this "binding" is mutual.  Here I understand "binding" is a kind
of association of an object and a reference that refers to it.)

This is _the_ point that I expected to find full explanation of reference
initialization (in the first part of 8.5.3/5).  A little further when
speaking of binding a reference to an rvalue or a temporary, it's said
*explicitly* the reference is bound to an object or sub-object.
Why isn't it said so here?
(It's probably still not exactly what I want, but at least gives a good
notion of what reference binding is.)

And I really do believe it, I just wanted to see how it was formally
defined.  Before I posted to this newsgroup I fell into a circular
sequence of chapter-references in the Standard trying to find the
final answer.

> >   which is plain wrong - it is bound to the sub-object within `b'
> >   (unless I wrongly understand the word "bound to" - I think it means
> >   "refers to").
>
> It is bound to the expression which is an lvalue.  How the address of
                                                         ^^^^^^^^^^^^^^
> that bound reference turns out to be the address of the subobject of
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> b is implementation magic.

That's exactly what I want!  Where does the Standard guarantee it?
IOW:  What guarantees: &static_cast<Base&>(d) == static_cast<Base*>(&d) ?
What the right-hand side value is (ie. what the pointer points to),
is described in 4.10/3 (Pointer conversions); I can't find similar
description for references in a proper place.



> > 10/1 (Derived classes)
> >   "[...] An lvalue of a derived class type can be bound to a
> >   reference to an accessible unambiguous base class [...]"
>
> >   Why lvalue and not sub-object (similar problem as just above)?
>
> Because binding is initialization and requires an expression which
> must be an lvalue in this case.
>
> >   Second question here:
> >   I understand that _initialization_ has to be unambiguous, but why
> >   binding?  The following:
> >     struct BB {};
> >     struct B1 {};
> >     struct B2 {};
> >     struct DD : B1, B2 {}  dd;
> >     BB& rbb = static_cast<B1&>(dd);
> >   seems okay, the initialization is disambiguated by explicit
> >   conversion, but a sub-object of `dd' is bound to `rbb' which is
> >   a reference to an _ambiguous_ base class.
>
> The static_cast explicitely binds the lvalue dd to an unnamed B1&.
> Since references are lvalues, that B1& lvalue may be bound to the BB&
> with no ambiguity.

I see, understanding "binding" as "initialization" makes more sense here.



> Your example is in the definition.  It is magic which happens in
> an initialization.

My example was taken from there, and explains the concept in the comment.
But examples aren't normative, are they?

--
Stan Tobias
sed 's/[A-Z]//g' to email

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: johnchx2@yahoo.com (johnchx)
Date: Tue, 29 Jun 2004 04:49:33 +0000 (UTC)
Raw View
sNOiSPAMt@amu.edu.pl ("S.Tobias") wrote

> So (not to lose the original question) where in the Standard is the
> definition of converting lvalue to, or initializing with lvalue,
> a reference to base?  Is there any other place that I didn't find?
> Or a rule that it can be inferred from?

I believe that 3.10/15 bullet #6 is the essential authorization to
refer to an object of derived type through an lvalue of base type.

-- John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Wed, 30 Jun 2004 18:48:28 +0000 (UTC)
Raw View
On Mon, 21 Jun 2004 16:57:44 +0000 (UTC), sNOiSPAMt@amu.edu.pl
("S.Tobias") wrote:

> [Example from 8.5.3/5]
>
>   struct A { };
>   struct B : public A { } b;
>   A& ra = b;                     // ra refers to A sub-object in b

> Where in the Standard is *described* conversion (or whatever)
> of derived class lvalue to reference to its base?

The problem may be that you are looking for something which does
not exist.  References are not converted (or whatevered).  There
is just some simple magic which happens with references.

> The closest I could get to are these:

> 4.10/3 (Pointer conversions)
>   Description is given, but for pointers (pointer to unambiguous
>   base class sub-object).

Right.  Pointers can be converted to other pointers, references
are never converted.

> 5.2.7/5 (Dynamic cast)
>   Description given for dynamic_cast ("lvalue for the unique B
>   sub-object").  Not applicable here.

Why is it not applicable?  Replacing the pointers with references
requires no conversion due to magic.

> 5.2.9/2 (Static cast)
>   static_cast relies on "T t(e);" and adds some more conversions
>   irrelevant here.

Yes, the magic here depends upon initialization.

> 8.5.3/5 (References)
>   Description (wrong?).  In the Example class A is obviously
>   reference-compatible with class B, so - according to the Standard
>   - the reference is bound to the initializer lvalue, ie. `b';

Absolutely.  This is the definition of the magic.  The standard says
it happens, believe it!

>   which is plain wrong - it is bound to the sub-object within `b'
>   (unless I wrongly understand the word "bound to" - I think it means
>   "refers to").

It is bound to the expression which is an lvalue.  How the address of
that bound reference turns out to be the address of the subobject of
b is implementation magic.

> 10/1 (Derived classes)
>   "[...] An lvalue of a derived class type can be bound to a
>   reference to an accessible unambiguous base class [...]"

>   Why lvalue and not sub-object (similar problem as just above)?

Because binding is initialization and requires an expression which
must be an lvalue in this case.

>   Second question here:
>   I understand that _initialization_ has to be unambiguous, but why
>   binding?  The following:
>     struct BB {};
>     struct B1 {};
>     struct B2 {};
>     struct DD : B1, B2 {}  dd;
>     BB& rbb = static_cast<B1&>(dd);
>   seems okay, the initialization is disambiguated by explicit
>   conversion, but a sub-object of `dd' is bound to `rbb' which is
>   a reference to an _ambiguous_ base class.

The static_cast explicitely binds the lvalue dd to an unnamed B1&.
Since references are lvalues, that B1& lvalue may be bound to the BB&
with no ambiguity.

Casts normally produce new rvalues.  In the case of reference casts,
there is no new thing, just some magic binding to magic references.
Note the careful wording in 5.2.10/10.  An lvalue is cast not converted
like rvalues.  The type bindings of lvalues can change without
conversion.

> So (not to lose the original question) where in the Standard is the
> definition of converting lvalue to, or initializing with lvalue,
> a reference to base?  Is there any other place that I didn't find?
> Or a rule that it can be inferred from?

Your example is in the definition.  It is magic which happens in
an initialization.

I hope this is as informative as amusing.

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: sNOiSPAMt@amu.edu.pl ("S.Tobias")
Date: Mon, 21 Jun 2004 16:57:44 +0000 (UTC)
Raw View
[Example from 8.5.3/5]

  struct A { };
  struct B : public A { } b;
  A& ra = b;                     // ra refers to A sub-object in b

Where in the Standard is *described* conversion (or whatever)
of derived class lvalue to reference to its base?

The closest I could get to are these:

4.10/3 (Pointer conversions)
  Description is given, but for pointers (pointer to unambiguous
  base class sub-object).

5.2.7/5 (Dynamic cast)
  Description given for dynamic_cast ("lvalue for the unique B
  sub-object").  Not applicable here.

5.2.9/2 (Static cast)
  static_cast relies on "T t(e);" and adds some more conversions
  irrelevant here.

8.5.3/5 (References)
  Description (wrong?).  In the Example class A is obviously
  reference-compatible with class B, so - according to the Standard
  - the reference is bound to the initializer lvalue, ie. `b';
  which is plain wrong - it is bound to the sub-object within `b'
  (unless I wrongly understand the word "bound to" - I think it means
  "refers to").

  Second description is further there, but for rvalue class
  initializers, which doesn't apply here.  The Standard says the
  reference is bound to object or sub-object (object being either
  rvalue or a temporary).

10/1 (Derived classes)
  "[...] An lvalue of a derived class type can be bound to a
  reference to an accessible unambiguous base class [...]"

  Why lvalue and not sub-object (similar problem as just above)?

  Second question here:
  I understand that _initialization_ has to be unambiguous, but why
  binding?  The following:
    struct BB {};
    struct B1 {};
    struct B2 {};
    struct DD : B1, B2 {}  dd;
    BB& rbb = static_cast<B1&>(dd);
  seems okay, the initialization is disambiguated by explicit
  conversion, but a sub-object of `dd' is bound to `rbb' which is
  a reference to an _ambiguous_ base class.

10.2/7 (Member name lookup)
  "[...] conversion from [...] lvalue of a derived class to
  [...] reference to one of its base classes shall unambiguously
  refer to a unique object representing the base class."

  Doesn't "object" mean "sub-object" here?


There are other places which refer to reference initialization/conversion,
but the above ones are the ones that define/should define the concept
of conversion of lvalue to reference to base (ie. that reference refers
to a sub-object, similarly to pointer conversion).  Note also that
some of the above places talk about "conversion", and some about
"initialization", but the "effect" should be the same.

Other conversions between different class types are copy c-tor or
conv fn based, and converge (not always) to being able to convert to,
or initialize, reference to base.

So (not to lose the original question) where in the Standard is the
definition of converting lvalue to, or initializing with lvalue,
a reference to base?  Is there any other place that I didn't find?
Or a rule that it can be inferred from?

--
Stan Tobias

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]