Topic: Can an rvalue be an lvalue?


Author: jpotter@falcon.lhup.edu (John Potter)
Date: Mon, 5 Jan 2004 02:07:01 +0000 (UTC)
Raw View
On Sat, 27 Dec 2003 19:05:47 +0000 (UTC), rani_sharoni@hotmail.com (Rani
Sharoni) wrote:

> jpotter@falcon.lhup.edu (John Potter) wrote in message news:<shuluvs837ngtv1cd3i4f6kilk241bppn4@4ax.com>...
> > On Thu, 25 Dec 2003 14:39:24 +0000 (UTC), thp@cs.ucr.edu wrote:
> >    & f(i);       // error lvalue required
> >    & (f(i) = i); // no error
> >
> > >     ++( 0 ? f(i) : i );    // Should increment i,

> > The type of the expression is determined at compile time.  It
> > can only be an lvalue if both the second and third arguments
> > are lvalues.  Since f(i) is an rvalue, an lvalue to rvalue
> > conversion is required on the other branch.  The point of the
> > post is that an lvalue to rvalue conversion produces a temporary
> > and modifying that temporary rvalue should not modify the original
> > object.  Other compilers fail the assertion.

> AFAICT there is no lvalue to rvalue conversion for class types.

That is not what 12.2 says about 4.1.  An lvalue to rvalue conversion
constructs a temporary.  All rvalues are either fabricated or copied
from lvalues.

> The
> equivalent to the non-class conversion is identity conversion and
> defined in terms of initialization per 13.3.3.1/6.

How do you pass a POD struct to a ... parameter?  That is the only
place in the standard other than 5.16 that I can find where an lvalue
to rvalue conversion is required for class types.  In both cases, there
is nothing to initialize yet there is a result.  As with all temporary
objects, they are rvalues.  It is required in 5.16 because there is no
function call.  The ?: operator is built into the language.

> There is an interesting paper about this issue:
> Eliminating the Class Rvalue Standard Conversion
> http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/1996/N0839.pdf

Please read the paper paying attention.  The subject of the paper is
derived rvalue to base rvalue conversion.  It has nothing to do with
the lvalue to rvalue conversion which applies equally well to class
types and fundamental types.  The paper is of historical interest
and the subject conversion was removed prior to standardization.

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: Mon, 5 Jan 2004 02:07:15 +0000 (UTC)
Raw View
On Mon, 29 Dec 2003 04:44:02 +0000 (UTC), rani_sharoni@hotmail.com (Rani
Sharoni) wrote:

> brok@rubikon.pl (Bronek Kozicki) wrote in message news:<1rucxal3p8s83$.1mgklqgrp5cho.dlg@40tude.net>...

> > On Sat, 27 Dec 2003 19:05:47 +0000 (UTC), Rani Sharoni wrote:
> > > AFAICT there is no lvalue to rvalue conversion for class types. The

> > I think that you are wrong. See clause 8.5.3:

> I meant for the following common case:
> struct A {} a;

int x; // assumed

> int f(A);
> int f(int);

> int x = f(a); // #1 implicit conversion - identity
> int y = f(x); // #2 implicit conversion - lvalue to rvalue

> 13.3.3.1/6 is quite clear about this distinguish from non-class types:
> When the parameter type is not a reference, the implicit conversion
> sequence models a copy-initialization of the parameter from the
> argument expression. [...] When the parameter has a class type and the
> argument expression has the same type, the implicit conversion
> sequence is an *identity* conversion.

int g (...);
int a = g(a); // lvalue to rvalue
int b = g(x); // lvalue to rvalue

Can we stop talking about overloading and discuss lvalue to rvalue
conversions explicitely required by the standard?

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: Mon, 5 Jan 2004 02:07:43 +0000 (UTC)
Raw View
On Wed, 31 Dec 2003 21:31:59 +0000 (UTC), rani_sharoni@hotmail.com
("Rani Sharoni") wrote:

> John Potter wrote:
> > On Mon, 29 Dec 2003 04:44:13 +0000 (UTC), rani_sharoni@hotmail.com
> > (Rani Sharoni) wrote:

> >> The question is not about optimization that eliminates temporaries.

> > Actually it is.  There are no non-temporary rvalues.

> rvalues and temporaries are different things that are usually confused to be
> the same thing.
> Rvalue is non-lvalue expression while temporary is object that created is
> various contexts.

We will continue to play word games for the amusement of others.  The
subject has the real question.  When an rvalue is created from an lvalue
can it refer to the same object?  Restated at end of article.

> For example:
> int main()
> {
>      void f();
>      f(); // rvalue expression of void type - no temporary is involved

Ok.  Special case of empty temporary.

>      int x;
>      x = 1; // 1 literal is rvalue (constant) expression - no temporary is
> involved

Ok.  We know not whether there is a temporary.

>      int const& r = 1; // rvalue expression results with temporary
>      r; //  lvalue expression that refers to temporary object

So?  No desire to debate about object which exist.

>      struct A {
>           A& self() {
>              // lvalue expression that might refer to temporary object
>              return *this;
>           }
>      };
>      A().self(); // lvalue expression that refers to temporary object

So?

> }
> Non-class rvalue is never cv-qualified while non-class temporary might be
> const.

Why is there no example?  It does not matter anyway.

> Here is interesting quote from DR #177:
> "The *temporary is an lvalue* and is used to initialize the variable..."

Not even the experts can remember that a temporary is an object and an
lvalue is an expression.  Amusing, but this "temporary" is an object
that the implementation must create to make exceptions work.  It does
not bother me to call its use an lvalue expression.  It does not matter
in this question.

> >> All the implementations I know don't introduce new temporary (of
> >> class type) when binding (short-lived) reference to rvalue.

> > An rvalue is a temporary, no need to introduce a new one.

> int f();
> int const& r = f(); // return value might be in register and therefore the
> standard mandate the introducing of additional const temporary per  8.5.3/5
> (non class case).

I don't care about non-class types.  Only modifiable rvalues are of
interest.

> As we both know even the class type case is allowed to produce additional
> *const* temporary.

More is fine.  The problem is none.

> >> This case is
> >> different since the question is whether new object should be
> >> introduced when the lvalue operand of the conditional operator
> >> becomes an rvalue:

> > Which it must since by definition, an rvalue is a temporary.

> Where did you saw such definition?

Finally, the crux of the question.  12.2 says that an lvalue to
rvalue conversion creates a temporary.  You continue to say
"becomes" which has no support in the standard.  We are talking
about the case where the standard says that an lvalue to rvalue
conversion is performed.  Can you find anything to say that a
required lvalue to rvalue conversion does not create a temporary
as an exception to 12.2?

> >> #include <iostream>
> >> #include <string>
> >> using namespace std;

> >> void f(string const&) {}

> >> int main() {
> >>     string str("what");
> >>     f( (true ? str : "force rvalue") += " next" ); // #1
> >>     cout << str << endl;
> >> }

> >> There are two reference bindings in the above #1 code:
> >> 1) When binding the resulting rvalue of ?: to the implicit object
> >> parameter in the += call. No additional temporary is allowed
> >> (obviously) allowed per 13.3.1/5/1.

> > Meaningless if there is no rvalue.  An lvalue is not an rvalue and
> > may not be used where an rvalue is required.  Producing an rvalue
> > from an lvalue produces a temporary.

> The conditional operator section (i.e. 5.16) describes iterative process in
> which the second and third operands are adjusted to be the same type and
> same expression kind. 5.16/3 explicitly mentions cases in which the lvalue
> is changed into an rvalue without copying so the standard is clear about
> such possibility.

I believe that bullet is defective and will submit the defect report.
Please avoid using it to justify not reading the other parts which apply
to the case in hand.

> It's not clear whether the "cond ? rvalue : rvalue" case can result with
> *additional* temporary that, for example, simplify the destruction
> bookkeeping. I didn't encounter any compiler that actually introduce
> additional temporary for that case.

I think we are talking about a fictional operator function which is
actually an operator built into the language.  For the purposes of
determining its appearance, we compare it to a function.  For class
types all of the conversion stuff will result in one of two things
with the same cv-qualification on all three.

C& operator? (bool, C&, C&)
C operator? (bool, C, C)

Once that is decided, we abandon the concept of a function and evaluate
the operator.  In the lvalue case, it evaluates the selected lvalue
expression.  In the rvalue case, it evaluates the selected rvalue
expression including the lvalue to rvalue conversion if needed.  If the
expression is already an rvalue, no further evaluation is needed.

> >> 2) When binding the f argument to the result of the += lvalue
> >> expression. This is usual direct binding per 8.5.3/5

> > Yes, because the operator returns an lvalue.  The parameter of
> > f could be non-const.  The function f adds nothing to the example.

> IMO it makes the example a bit more realistic.

> >> The problem is what happens when the second lvalue operand of ?: is
> >> transformed into the rvalue result of ?:. IMO the standard should be
> >> clear about whether this transformation is actually conversion and
> >> should result with new object since it's obviously changes the
> >> meaning of the program regardless of any optimizations (i.e.
> >> eliminations of temporaries). I think, and John Potter disagree
> >> about it, that the (explicit) intention of 5.16/3 is clear and the
> >> transformation does not introduce new object.

> > You have concocted yet another example where the error in 5.16/3 does
> > not apply.

> I think that it does.

Please correct the following.

> > second is string lvalue and third is array of const char lvalue
> > 5.16/3 Different types with at least one class? yes
> > 5.16/3/1 E2 an lvalue? yes for both directions
> >          neither can be converted to an lvalue of the other type.
> > 5.16/3/2 E2 an rvalue or above failed?  Above failed.
> > 5.16/3/2/1 Both class types? No.
> > 5.16/3/2/2 Conversion possible?  Yes.
> >        The array is converted to a string temporary rvalue.
> > 5.16/4 Two lvalues of same type?  No.
> > 5.16/5 The result is an rvalue.  Different types?  No.

> I figured that the process is more iterative.
> The problem with 5.16/3 is that although it starts with "different types" it
> describes cases in which the types are the *same* and this is confusing and
> worth clarification.

There are no cases where the types are the same.  The underlying types
may be the same but when cv-qualifiers are added, the types are
different.  C is the same type as C, but C const is not the same type
as C.  I used derived types in my examples because I needed modifiable
rvalues.  You may note that there is no way to get the cv-qualifications
in line if we skip 5.16/3.  We can have different underlying types
and/or different cv-qualifiers.  There must be something different.

> >> The lvalue is simple changed into rvalue.

> > This is simply not a possibility.  An lvalue is an address and an
> > rvalue is a value which has no address.  An rvalue is a temporary for
> > class types and exists in the vapor for builtins.  In no case does a
> > modification of an rvalue modify the lvalue source of its existence.

> Inside the constructor (or member function) *this which might refer to
> temporary is an lvalue. This lvalue refers to an object that later might be
> referred by rvalue.

> The process of binding *this to rvalue (i.e. A().f()) obviously involves the
> address of the rvalue. Nothing new here.

> I admit that directly changing lvalue into rvalue as mentioned in 5.16/3 is
> quite unique but operator?: is also no of a kind.

We can agree on that.  The only thing close is ...

> >> [...]
> > Replace the rvalue A(10) with an lvalue a and GCC will complain that
> > it can not perform the lvalue to rvalue conversion because the copy
> > ctor is private.  Also replace A(11) with an lvalue a2 and it is
> > again valid because it becomes the operator on lvalues.

> I wonder what the GCC developer thinks about this issue since I suspect that
> they might want to fix this one.

> > Your entire logic is based on an rvalue being something that refers to
> > the same object as an lvalue.  I find no support for this in the
> > standard other than the defect in 5.16/3.

> It really easy to doubt me but not EDG and VC that thinks the same.

The results I got in clc++m are interesting.  On one side, we have
EDG creating no temporaries.  In one of the three cases, it even
allows taking the address of the rvalue.  In the other two cases
it complained lvalue required.  On the other side we have GCC-2.95
and GCC-3.4 creating temporaries in all three cases.  Interesting
that 3.4 has returned to the state of 2.95 after several versions
that did not create a temporary in one of the cases.  Almost all
other implementations did not create temporaries when 5.16/3/2/1 did
not apply and did create a temporary when it did apply.  They simply
refused to change an lvalue of one type into an rvalue of another
type refering to the original object.

The basic question remains.  When the standard says that an lvalue
to rvalue conversion is performed is a temporary copy required as
stated in 12.2?

John

For those who may have missed the clc++m example, here is a copy.

#include <cassert>
struct B {
    int v;
    B (int v) : v(v) { }
    void inc () { ++ v; }
    };
struct D : B {
    D (int v) : B(v) { }
    };
int main () {
    B b1(42);
    D d1(42);
    (0 ? B(13) : b1).inc();
    assert(b1.v == 42);
    (0 ? D(13) : b1).inc();
    assert(b1.v == 42);
    (0 ? B(13) : d1).inc();
    assert(d1.v == 42);
    }

// CbuilderX(EDG301) FFF  Rob Williscroft
// ICC-8.0           FFF  Alexander Stippler
// COMO-4.301        FFF  Alexander Stippler

// BCC-5.4           FFP  Rob Williscroft
// BCC32-5.5         FFP  John Potter
// BCC32-5.65        FFP  Rob Williscroft
// VC-6.0            FFP  Stephen Howe
// VC-7.0            FFP  Ben Hutchings
// VC-7.1            FFP  Stephen Howe
// OpenWatcom-1.1    FFP  Stephen Howe

// Sun C++-6.2       PFF  Ron Natalie

// GCC-3.2           PFP  John Potter
// GCC-3.3           PFP  Alexander Stippler

// GCC-2.95          PPP  Ben Hutchings
// GCC-3.4           PPP  Florian Weimer

---
[ 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: rani_sharoni@hotmail.com ("Rani Sharoni")
Date: Mon, 5 Jan 2004 09:47:44 +0000 (UTC)
Raw View
John Potter wrote:
> On Mon, 29 Dec 2003 04:44:02 +0000 (UTC), rani_sharoni@hotmail.com
> (Rani
> Sharoni) wrote:
>
>> brok@rubikon.pl (Bronek Kozicki) wrote in message
>> news:<1rucxal3p8s83$.1mgklqgrp5cho.dlg@40tude.net>...
>
>>> On Sat, 27 Dec 2003 19:05:47 +0000 (UTC), Rani Sharoni wrote:
>>>> AFAICT there is no lvalue to rvalue conversion for class types. The
>
>>> I think that you are wrong. See clause 8.5.3:
>
>> I meant for the following common case:
>> struct A {} a;
>
> int x; // assumed

I actually did defined x.

>> int f(A);
>> int f(int);
>
>> int x = f(a); // #1 implicit conversion - identity
>> int y = f(x); // #2 implicit conversion - lvalue to rvalue
>
>> 13.3.3.1/6 is quite clear about this distinguish from non-class
>> types:
>> When the parameter type is not a reference, the implicit conversion
>> sequence models a copy-initialization of the parameter from the
>> argument expression. [...] When the parameter has a class type and
>> the
>> argument expression has the same type, the implicit conversion
>> sequence is an *identity* conversion.
>
> int g (...);
> int a = g(a); // lvalue to rvalue
> int b = g(x); // lvalue to rvalue
>
> Can we stop talking about overloading and discuss lvalue to rvalue
> conversions explicitely required by the standard?

In terms of overloading the first conversion sequence is ellipsis that
requires lvalue to rvalue conversion (per 5.2.2/7) which is identity
conversion sequence for class types but I agree that overloading is not
relevant in this discussion.

Rani


---
[ 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: rani_sharoni@hotmail.com ("Rani Sharoni")
Date: Mon, 5 Jan 2004 09:47:49 +0000 (UTC)
Raw View
John Potter wrote:
> On Sat, 27 Dec 2003 19:05:47 +0000 (UTC), rani_sharoni@hotmail.com
> (Rani Sharoni) wrote:
>> AFAICT there is no lvalue to rvalue conversion for class types.
>
> That is not what 12.2 says about 4.1.  An lvalue to rvalue conversion
> constructs a temporary.  All rvalues are either fabricated or copied
> from lvalues.

My original statement is obviously incomplete. I meant to say that there is
no lvalue to rvalue *standard* conversion and this is actually only matters
for overloading (i.e. in perspective of overloading this lvalue to rvalue
conversion is identity conversion and defined in terms of initialization)
which is a bit off topic. Sorry.

>> The
>> equivalent to the non-class conversion is identity conversion and
>> defined in terms of initialization per 13.3.3.1/6.
>
> How do you pass a POD struct to a ... parameter?  That is the only
> place in the standard other than 5.16 that I can find where an lvalue
> to rvalue conversion is required for class types.  In both cases,
> there
> is nothing to initialize yet there is a result.  As with all temporary
> objects, they are rvalues.  It is required in 5.16 because there is no
> function call.  The ?: operator is built into the language.

Which makes me think that user-defined ?: might be real nightmare in terms
of overloading.

>> There is an interesting paper about this issue:
>> Eliminating the Class Rvalue Standard Conversion
>> http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/1996/N0839.pdf
>
> Please read the paper paying attention.  The subject of the paper is
> derived rvalue to base rvalue conversion.  It has nothing to do with
> the lvalue to rvalue conversion which applies equally well to class
> types and fundamental types.  The paper is of historical interest
> and the subject conversion was removed prior to standardization.

This paper also proves that the lvalue to rvalue conversion is different for
class types yet I admit that it's not very relevant in this discussion.

Rani


---
[ 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: rani_sharoni@hotmail.com ("Rani Sharoni")
Date: Mon, 5 Jan 2004 17:23:04 +0000 (UTC)
Raw View
John Potter wrote:
> ("Rani Sharoni") wrote:
>> John Potter wrote:
>>> (Rani Sharoni) wrote:
>>> Which it must since by definition, an rvalue is a temporary.
>
>> Where did you saw such definition?
>
> Finally, the crux of the question.  12.2 says that an lvalue to
> rvalue conversion creates a temporary.  You continue to say
> "becomes" which has no support in the standard.  We are talking
> about the case where the standard says that an lvalue to rvalue
> conversion is performed.  Can you find anything to say that a
> required lvalue to rvalue conversion does not create a temporary
> as an exception to 12.2?
> [...]
>> The conditional operator section (i.e. 5.16) describes iterative
>> process in which the second and third operands are adjusted to be
>> the same type and same expression kind. 5.16/3 explicitly mentions
>> cases in which the lvalue is changed into an rvalue without copying
>> so the standard is clear about such possibility.
>
> I believe that bullet is defective and will submit the defect report.
> Please avoid using it to justify not reading the other parts which
> apply  to the case in hand.

IMO before submitting the defect report you must also consider the potential
overhead when *mandating* the additional temporary. I'm not sure that
current implementations will be happy to change their current "optimized"
behavior without any clear benefit especially due to potential backward
compatibilities issues.
This might just (sadly) remain one of the C++ cases where the programmer
must be more careful.

Thanks,
Rani


---
[ 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: pasa@lib.hu ("Balog Pal")
Date: Tue, 6 Jan 2004 03:00:50 +0000 (UTC)
Raw View
""Rani Sharoni"" <rani_sharoni@hotmail.com> wrote in message
news:3ff929e2@news.microsoft.com...

> IMO before submitting the defect report you must also consider the
potential
> overhead when *mandating* the additional temporary. I'm not sure that
> current implementations will be happy to change their current "optimized"
> behavior without any clear benefit especially due to potential backward
> compatibilities issues.
> This might just (sadly) remain one of the C++ cases where the programmer
> must be more careful.

What that has to do with _submitting_ the DR? It's the committee's job to
consider a rensible resolution. Selecting some behavior from the pool.

Why to have a standard if then anything goes?  How can one rely upon?

Paul


---
[ 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: Tue, 23 Dec 2003 02:07:49 +0000 (UTC)
Raw View
Assume that deque::iterator is user defined with a member prefix
increment.

#include <cassert>
#include <deque>
using namespace std;
int main () {
    deque<int> d(4, 2);
    deque<int>::iterator it(d.begin());
    ++ (d.begin() == d.end() ? d.begin() : it);
    assert(it == d.begin());
    }

Because the second operand of the conditional expression is an
rvalue, the value of the conditional expression is an rvalue.

Must the assertion pass?

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: rani_sharoni@hotmail.com (Rani Sharoni)
Date: Tue, 23 Dec 2003 17:05:45 +0000 (UTC)
Raw View
jpotter@falcon.lhup.edu (John Potter) wrote in message news:<l80fuvg7ebnp8p8rb60j8id7qn1juci5ld@4ax.com>...
> Assume that deque::iterator is user defined with a member prefix
> increment.
>
> #include <cassert>
> #include <deque>
> using namespace std;
> int main () {
>     deque<int> d(4, 2);
>     deque<int>::iterator it(d.begin());
>     ++ (d.begin() == d.end() ? d.begin() : it);
>     assert(it == d.begin());
>     }
>
> Because the second operand of the conditional expression is an
> rvalue, the value of the conditional expression is an rvalue.
>
> Must the assertion pass?

AFAICT the assertion must fail.

As we already noticed in different post that 5.16/3/2/1 specifies that
in the above case the class type lvalue is *changed* into rvalue that
refers to the original object which means that no copy is allowed.
13.3.1/5 forbid to introduce new temporary to hold the argument for
the implicit object parameter (i.e. *this) and therefore operator++()
affect the original "it" object. Notice that the result the expression
(the ++ return value) is non const lvalue that refers to "it" and can
bounded to *non-const* reference.

IMO EDG, VC and BCC are right and GCC is wrong. I guess that the GCC
developers will be happy to fix this bug especially due to potential
performance issues that arise from unnecessary coping.

Rani

template<typename T> // ct_assert is_class<T>
T dummy(T& x) { return x; }

// valid for lvalues of class types
#define TO_RVALUE(e) (true ? e : dummy(e))

---
[ 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, 24 Dec 2003 06:45:24 +0000 (UTC)
Raw View
On Tue, 23 Dec 2003 17:05:45 +0000 (UTC), rani_sharoni@hotmail.com (Rani
Sharoni) wrote:

> jpotter@falcon.lhup.edu (John Potter) wrote in message news:<l80fuvg7ebnp8p8rb60j8id7qn1juci5ld@4ax.com>...

> > #include <cassert>
> > #include <deque>
> > using namespace std;
> > int main () {
> >     deque<int> d(4, 2);
> >     deque<int>::iterator it(d.begin());
> >     ++ (d.begin() == d.end() ? d.begin() : it);
> >     assert(it == d.begin());
> >     }

> > Because the second operand of the conditional expression is an
> > rvalue, the value of the conditional expression is an rvalue.

> > Must the assertion pass?

> AFAICT the assertion must fail.

I obviously disagree.  Let's walk through it.

> As we already noticed in different post that 5.16/3/2/1 specifies that
> in the above case the class type lvalue is *changed* into rvalue that
> refers to the original object which means that no copy is allowed.

second: d.begin() an rvalue of type deque<int>::iterator
third:  it an lvalue of type deque<int>::iterator

5.16
/2 neither is void does not apply
/3 types are the same does not apply
/4 second is an rvalue does not apply
/5 the result is an rvalue, types are the same rest does not apply
/6 lvalue to rvalue conversions are performed

12.2 temporaries of class type are created ... a conversion that creates
an rvalue.

Can it be removed?

(2003)12.8/15.  It is neither the return value of a function nor being
copied into another object.  Does not apply.

As-if.  I can tell.  Does not apply.

Now for something which will follow your logic.

struct B { int x; };
struct D : B { };
int main () {
   D d;
   d.x = 42;
   B b = { 69 };
   (true ? B() : d) = b;
   assert(d.x == 42);
   }

second: B() rvalue of type B
third:  d lvalue of type D

5.16
/2 neither is void does not apply
/3 are of different class types it applies
   second will not convert to third in any way
   /1 second is an rvalue does not apply
   /2 applies
      /1 applies and third is converted to an rvalue of type B
         refering to the B part of the original D
/4 both are rvalues does not apply
/5 the result is an rvalue, types are the same rest does not apply
/6 lvalue to rvalue conversions are performed, there are no lvalues does
not apply

G++ passes the assert.  BCC does not know that operator= works on
rvalues.  Comeau online accepts it including when a private copy
ctor is added to B.

I think there is a clear defect in 5.16/3 when it claims to convert an
lvalue to rvalue without creating a temporary in contradiction to the
rest of the standard.  If the expression were already an rvalue,
changing the type without a copy would be fine. If the lvalue expression
were converted to an lvalue of type B as is normal with derived to base
conversions, it would make sense and agree with the other case.  I think
the lvalue to rvalue conversion without a copy went unnoticed when
writing that clause.

When modification of the rvalue of an object modifies the object,
something is very wrong.  Modifiable rvalues are fine, but not
when we can't tell the lvalue and rvlaue of an object apart.

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: thp@cs.ucr.edu
Date: Thu, 25 Dec 2003 14:39:24 +0000 (UTC)
Raw View
John Potter <jpotter@falcon.lhup.edu> wrote:
+ Assume that deque::iterator is user defined with a member prefix
+ increment.
+
+ #include <cassert>
+ #include <deque>
+ using namespace std;
+ int main () {
+    deque<int> d(4, 2);
+    deque<int>::iterator it(d.begin());
+    ++ (d.begin() == d.end() ? d.begin() : it);
+    assert(it == d.begin());
+    }
+
+ Because the second operand of the conditional expression is an
+ rvalue, the value of the conditional expression is an rvalue.
+
+ Must the assertion pass?

FWIW, under g++ (3.0.4), this simplified variant of your code
compiles and passes the assertion:

  #include <cassert>
  #include <deque>
  using namespace std;

  typedef deque<int>::iterator Iter;
  Iter f(Iter& x) { return x; }

  int main () {
    deque<int> d(4,2);
    Iter i = d.begin();
    f(i) = i;          // So "f(i)" is an lvalue!
    ++( 0 ? f(i) : i );    // Should increment i,
    assert( i == d.begin() );     // but doesn't!
  }

When I modify f to return "by reference" the assertion fails.

Tom Payne

---
[ 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, 27 Dec 2003 03:25:32 +0000 (UTC)
Raw View
On Thu, 25 Dec 2003 14:39:24 +0000 (UTC), thp@cs.ucr.edu wrote:

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

> + Assume that deque::iterator is user defined with a member prefix
> + increment.

> + #include <cassert>
> + #include <deque>
> + using namespace std;
> + int main () {
> +    deque<int> d(4, 2);
> +    deque<int>::iterator it(d.begin());
> +    ++ (d.begin() == d.end() ? d.begin() : it);
> +    assert(it == d.begin());
> +    }

> + Because the second operand of the conditional expression is an
> + rvalue, the value of the conditional expression is an rvalue.

> + Must the assertion pass?

> FWIW, under g++ (3.0.4), this simplified variant of your code
> compiles and passes the assertion:

As it should.  Please read 5.16 carefully.

>   #include <cassert>
>   #include <deque>
>   using namespace std;

>   typedef deque<int>::iterator Iter;
>   Iter f(Iter& x) { return x; }

This function returns an rvalue.

>   int main () {
>     deque<int> d(4,2);
>     Iter i = d.begin();
>     f(i) = i;          // So "f(i)" is an lvalue!

No, assignment to an rvalue is valid for user defined types
because operator= is a member function.  Rvalues may be
modified by non-const member functions.  See 3.10.  This is
the famous rvalue to lvalue conversion because operator=
returns T& which is an lvalue.

   & f(i);       // error lvalue required
   & (f(i) = i); // no error

>     ++( 0 ? f(i) : i );    // Should increment i,

The type of the expression is determined at compile time.  It
can only be an lvalue if both the second and third arguments
are lvalues.  Since f(i) is an rvalue, an lvalue to rvalue
conversion is required on the other branch.  The point of the
post is that an lvalue to rvalue conversion produces a temporary
and modifying that temporary rvalue should not modify the original
object.  Other compilers fail the assertion.

>     assert( i == d.begin() );     // but doesn't!
>   }

> When I modify f to return "by reference" the assertion fails.

Yes, a reference is always an lvalue.  You then change the type of
the conditional expression to an lvalue and the assertion must
fail for either branch.

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: rani_sharoni@hotmail.com (Rani Sharoni)
Date: Sat, 27 Dec 2003 19:05:47 +0000 (UTC)
Raw View
jpotter@falcon.lhup.edu (John Potter) wrote in message news:<shuluvs837ngtv1cd3i4f6kilk241bppn4@4ax.com>...
> On Thu, 25 Dec 2003 14:39:24 +0000 (UTC), thp@cs.ucr.edu wrote:
>    & f(i);       // error lvalue required
>    & (f(i) = i); // no error
>
> >     ++( 0 ? f(i) : i );    // Should increment i,
>
> The type of the expression is determined at compile time.  It
> can only be an lvalue if both the second and third arguments
> are lvalues.  Since f(i) is an rvalue, an lvalue to rvalue
> conversion is required on the other branch.  The point of the
> post is that an lvalue to rvalue conversion produces a temporary
> and modifying that temporary rvalue should not modify the original
> object.  Other compilers fail the assertion.

AFAICT there is no lvalue to rvalue conversion for class types. The
equivalent to the non-class conversion is identity conversion and
defined in terms of initialization per 13.3.3.1/6.

There is an interesting paper about this issue:
Eliminating the Class Rvalue Standard Conversion
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/1996/N0839.pdf

Rani

---
[ 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: thp@cs.ucr.edu
Date: Sun, 28 Dec 2003 03:04:26 +0000 (UTC)
Raw View
John Potter <jpotter@falcon.lhup.edu> wrote:
+ On Thu, 25 Dec 2003 14:39:24 +0000 (UTC), thp@cs.ucr.edu wrote:
+
+> John Potter <jpotter@falcon.lhup.edu> wrote:
+
+> + Assume that deque::iterator is user defined with a member prefix
+> + increment.
+
+> + #include <cassert>
+> + #include <deque>
+> + using namespace std;
+> + int main () {
+> +    deque<int> d(4, 2);
+> +    deque<int>::iterator it(d.begin());
+> +    ++ (d.begin() == d.end() ? d.begin() : it);
+> +    assert(it == d.begin());
+> +    }
+
+> + Because the second operand of the conditional expression is an
+> + rvalue, the value of the conditional expression is an rvalue.
+
+> + Must the assertion pass?
+
+> FWIW, under g++ (3.0.4), this simplified variant of your code
+> compiles and passes the assertion:
+
+ As it should.  Please read 5.16 carefully.
+
+>   #include <cassert>
+>   #include <deque>
+>   using namespace std;
+
+>   typedef deque<int>::iterator Iter;
+>   Iter f(Iter& x) { return x; }
+
+ This function returns an rvalue.
+
+>   int main () {
+>     deque<int> d(4,2);
+>     Iter i = d.begin();
+>     f(i) = i;          // So "f(i)" is an lvalue!
+
+ No, assignment to an rvalue is valid for user defined types
+ because operator= is a member function.  Rvalues may be
+ modified by non-const member functions.  See 3.10.  This is
+ the famous rvalue to lvalue conversion because operator=
+ returns T& which is an lvalue.
+
+   & f(i);       // error lvalue required
+   & (f(i) = i); // no error
+
+>     ++( 0 ? f(i) : i );    // Should increment i,
+
+ The type of the expression is determined at compile time.  It
+ can only be an lvalue if both the second and third arguments
+ are lvalues.  Since f(i) is an rvalue, an lvalue to rvalue
+ conversion is required on the other branch.  The point of the
+ post is that an lvalue to rvalue conversion produces a temporary
+ and modifying that temporary rvalue should not modify the original
+ object.

I understand how ++ can be made to operate on deque rvalues, but doing
so seems a bit weird for some reason.  I would think that passing the
argument by (nonconst) reference would be more natural.  In such a
case, these examples wouldn't compile -- I hope.

Tom Payne

---
[ 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: brok@rubikon.pl (Bronek Kozicki)
Date: Sun, 28 Dec 2003 03:04:47 +0000 (UTC)
Raw View
On Sat, 27 Dec 2003 03:25:32 +0000 (UTC), John Potter wrote:
> The point of the
> post is that an lvalue to rvalue conversion produces a temporary
> and modifying that temporary rvalue should not modify the original
> object.  Other compilers fail the assertion.

Not the first time. See example I posted in thread "Comment on core
issue 391 October 2003 note". There is reference bound to rvalue (which
is "kind of" rvalue to lvalue conversion, as in your example). According
to 8.5.3, this reference should be bound to temporary. Clearly, 12.8
rules do not apply here. In spite of this, most compilers "optimize
away" this temporary, which unfortunately changes meaning of whole
program (that's why I call it "optimization", not real optimization).

Possibly it means that there should be additional note in these few
clauses of standard (5.16, 8.5.3) which makes it clear, that creation of
this temporary is obligatory. DR, anyone ?


B.

---
[ 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: brok@rubikon.pl (Bronek Kozicki)
Date: Sun, 28 Dec 2003 03:05:07 +0000 (UTC)
Raw View
On Sat, 27 Dec 2003 19:05:47 +0000 (UTC), Rani Sharoni wrote:
> AFAICT there is no lvalue to rvalue conversion for class types. The

I think that you are wrong. See clause 8.5.3:

If the initializer expression
- [...]
=A1X has a class type (i.e., T2 is a class type) and can be implicitly
  converted to an lvalue of type =A1=A7cv3 T3,=A1=A8 where =A1=A7cv1 T1=A1=
=A8 is
  reference-compatible with =A1=A7cv3 T3=A1=A8 *(92) (this conversion is
  selected by enumerating the applicable conversion functions (13.3.1.6)
  and choosing the best one through overload resolution (13.3)),
then [...] the reference is bound to the lvalue result of the conversion
in the second case.

(92) This requires a conversion function (12.3.2) returning a reference
type.


B.

---
[ 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: rani_sharoni@hotmail.com (Rani Sharoni)
Date: Mon, 29 Dec 2003 04:44:02 +0000 (UTC)
Raw View
brok@rubikon.pl (Bronek Kozicki) wrote in message news:<1rucxal3p8s83$.1mgklqgrp5cho.dlg@40tude.net>...
> On Sat, 27 Dec 2003 19:05:47 +0000 (UTC), Rani Sharoni wrote:
> > AFAICT there is no lvalue to rvalue conversion for class types. The
>
> I think that you are wrong. See clause 8.5.3:

I meant for the following common case:
struct A {} a;
int f(A);
int f(int);

int x = f(a); // #1 implicit conversion - identity
int y = f(x); // #2 implicit conversion - lvalue to rvalue

13.3.3.1/6 is quite clear about this distinguish from non-class types:
When the parameter type is not a reference, the implicit conversion
sequence models a copy-initialization of the parameter from the
argument expression. [...] When the parameter has a class type and the
argument expression has the same type, the implicit conversion
sequence is an *identity* conversion.

Rani

---
[ 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: rani_sharoni@hotmail.com (Rani Sharoni)
Date: Mon, 29 Dec 2003 04:44:13 +0000 (UTC)
Raw View
brok@rubikon.pl (Bronek Kozicki) wrote in message news:<10ipje5s2iv3r.nmeif9c1zckd$.dlg@40tude.net>...
> On Sat, 27 Dec 2003 03:25:32 +0000 (UTC), John Potter wrote:
> > The point of the
> > post is that an lvalue to rvalue conversion produces a temporary
> > and modifying that temporary rvalue should not modify the original
> > object.  Other compilers fail the assertion.
>
> Not the first time. See example I posted in thread "Comment on core
> issue 391 October 2003 note". There is reference bound to rvalue (which
> is "kind of" rvalue to lvalue conversion, as in your example). According
> to 8.5.3, this reference should be bound to temporary. Clearly, 12.8
> rules do not apply here. In spite of this, most compilers "optimize
> away" this temporary, which unfortunately changes meaning of whole
> program (that's why I call it "optimization", not real optimization).

The question is not about optimization that eliminates temporaries.
All the implementations I know don't introduce new temporary (of class
type) when binding (short-lived) reference to rvalue. This case is
different since the question is whether new object should be
introduced when the lvalue operand of the conditional operator becomes
an rvalue:
#include <iostream>
#include <string>
using namespace std;

void f(string const&) {}

int main() {
    string str("what");
    f( (true ? str : "force rvalue") += " next" ); // #1
    cout << str << endl;
}

There are two reference bindings in the above #1 code:
1) When binding the resulting rvalue of ?: to the implicit object
parameter in the += call. No additional temporary is allowed
(obviously) allowed per 13.3.1/5/1.
2) When binding the f argument to the result of the += lvalue
expression. This is usual direct binding per 8.5.3/5

The problem is what happens when the second lvalue operand of ?: is
transformed into the rvalue result of ?:. IMO the standard should be
clear about whether this transformation is actually conversion and
should result with new object since it's obviously changes the meaning
of the program regardless of any optimizations (i.e. eliminations of
temporaries). I think, and John Potter disagree about it, that the
(explicit) intention of 5.16/3 is clear and the transformation does
not introduce new object. The lvalue is simple changed into rvalue.
Now we are back to the "cond ? rvalue : rvalue" case.
For example:
struct A {
    explicit A(int);
    int val() const;

private:
    A(A const&);
};

extern bool b;
int  x = (true ? A(10) : A(11)).val();

EDG, VC, BCC and even GCC found the above code well-formed but it's
not clear from the standard whether additional "bookkeeping
simplification" conversion is allowed. IMO in this case compilers will
make the extra effort to eliminate unnecessarily temporaries due to
performance concerns (i.e. std::string expensive copying). This also
shows that GCC makes such optimizations and this is not an
optimization issue.

I think that the standard should consider the expectations of the
programmer and choose one deterministic behavior. IMO since it's not
possible to have both options (i.e. avoiding temporaries) and
therefore the preferred option should obviously be not copying at all
which is consistent with DR #391. In case that the rvalue (lifetime)
is bounded to a (long-lived) reference (i.e. A const& r = (cond ? a :
A())) then additional temporary must be introduce since it's not
possible to shorten the lifetime of lvalue and indeed VC and GCC that
support this binding (i.e. extending the lifetime of the rvalue)
behave like that.

> Possibly it means that there should be additional note in these few
> clauses of standard (5.16, 8.5.3) which makes it clear, that creation of
> this temporary is obligatory. DR, anyone ?

I hope that I'll have time to file a DR about this issue but I'll be
happy to do if with someone less bride than me.

Thanks,
Rani

---
[ 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: Mon, 29 Dec 2003 04:44:23 +0000 (UTC)
Raw View
On Sun, 28 Dec 2003 03:04:26 +0000 (UTC), thp@cs.ucr.edu wrote:

> I understand how ++ can be made to operate on deque rvalues, but doing
> so seems a bit weird for some reason.

Do I remember you having a dislike for modifiable rvalues?  ;)

Any container other than vector/string will likely work.  For some
reason, there is a guideline that states operators which modify an
object should be members.

> I would think that passing the
> argument by (nonconst) reference would be more natural.  In such a
> case, these examples wouldn't compile -- I hope.

You are talking about the this parameter of the member function.  Now
all you need to do is teach all library implementers to use friend
functions for all operators except those required to be members.  The
standard leaves it open for iterators.

template <class T>
struct ListIterator {
   ListNode<T>* ptr;
   friend ListIterator& operator++ (ListIterator& it) {
      it.ptr = it.ptr->next;
      return it;
      }
   // ...
   };

Note that it will be instantiated whether used or not.  To make it a
template defined elsewhere requires a bit more work and introduces
some interesting problems sometimes.

While you are at it, also convince the committee to remove the
requirements for some operators being members.  Operator= is the
dangerous one because it gives the rvalue to lvalue conversion.

   ++ (ListIterator<int>() = ListIterator<int>());

Anyway, the point of this thread is that an lvalue to rvalue conversion
creates a temporary.  Modification of the temporary may not modify the
original.  This does not seem to be the case in some implementations of
operator?:.  It also seems unclear for some cases in the standard.

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: Mon, 29 Dec 2003 05:28:01 +0000 (UTC)
Raw View
On Sun, 28 Dec 2003 03:04:47 +0000 (UTC), brok@rubikon.pl (Bronek
Kozicki) wrote:

> On Sat, 27 Dec 2003 03:25:32 +0000 (UTC), John Potter wrote:

> > The point of the
> > post is that an lvalue to rvalue conversion produces a temporary
> > and modifying that temporary rvalue should not modify the original
> > object.  Other compilers fail the assertion.

> Not the first time. See example I posted in thread "Comment on core
> issue 391 October 2003 note". There is reference bound to rvalue (which
> is "kind of" rvalue to lvalue conversion, as in your example).

Not the same.  A const reference bound to an rvalue as opposed to a
non-const reference refering to what an rvalue refers.  Also not the
same as an rvalue refering to a named object.

> According to 8.5.3, this reference should be bound to temporary.

Agreed.

> Clearly, 12.8 rules do not apply here.

Agreed.

> In spite of this, most compilers "optimize
> away" this temporary, which unfortunately changes meaning of whole
> program (that's why I call it "optimization", not real optimization).

A related bug in the compilers.

> Possibly it means that there should be additional note in these few
> clauses of standard (5.16, 8.5.3) which makes it clear, that creation of
> this temporary is obligatory. DR, anyone ?

I think that 8.5.3 is quite clear and the compilers are just buggy.  I
don't see anything in your issue 434.  Kanze stated the case quite
clearly in this newsgroup.  The note that you want changed appears in
several other places as well.

I also think that 5.16 is quite clear on the same type rvalue and lvalue
case and the compilers are just buggy.  I think there is a problem in
5.16/3 for an rvalue and lvalue of related types.  Getting the wording
right will not be easy.  I will try.

The major problem seems to be the construction of temporaries.  It would
be nice to say that all conversions create temporaries; however, most
conversions work on rvalues.  Who cares whether the conversion creates
a new rvalue or changes the type of the old rvalue?  It makes no
difference because the address of an rvalue is not available and there
is no way to tell.  The problem is that conversion from an lvalue to an
rvalue must produce a temporary.

In the following, lvalues are designated by &.  Others are rvalues.

Struct B { };
Struct D : B { };
B -> B const : magic on rvalues
B* -> B const* : qualification conversion on rvalues
B -> B const& : binding to an rvalue, not a conversion
B& -> B const& : magic on lvalues
D -> B : magic on rvalues
D* -> B* : pointer conversion on rvalues
D& -> B& : magic on lvalues
B& -> B : lvalue to rvalue conversion constructs a temporary

B*& -> B const* : without temporary nonsense

This seems to be your concern in 434, but I see nothing to
allow it in the standard.

D& -> B : without temporary nonsense
B& -> B const : without temporary nonsense

These last two seem to be allowed by 5.16/3

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: Tue, 30 Dec 2003 05:05:26 +0000 (UTC)
Raw View
On Mon, 29 Dec 2003 04:44:13 +0000 (UTC), rani_sharoni@hotmail.com (Rani
Sharoni) wrote:

> The question is not about optimization that eliminates temporaries.

Actually it is.  There are no non-temporary rvalues.

> All the implementations I know don't introduce new temporary (of class
> type) when binding (short-lived) reference to rvalue.

An rvalue is a temporary, no need to introduce a new one.

> This case is
> different since the question is whether new object should be
> introduced when the lvalue operand of the conditional operator becomes
> an rvalue:

Which it must since by definition, an rvalue is a temporary.

> #include <iostream>
> #include <string>
> using namespace std;

> void f(string const&) {}

> int main() {
>     string str("what");
>     f( (true ? str : "force rvalue") += " next" ); // #1
>     cout << str << endl;
> }

> There are two reference bindings in the above #1 code:
> 1) When binding the resulting rvalue of ?: to the implicit object
> parameter in the += call. No additional temporary is allowed
> (obviously) allowed per 13.3.1/5/1.

Meaningless if there is no rvalue.  An lvalue is not an rvalue and
may not be used where an rvalue is required.  Producing an rvalue
from an lvalue produces a temporary.

> 2) When binding the f argument to the result of the += lvalue
> expression. This is usual direct binding per 8.5.3/5

Yes, because the operator returns an lvalue.  The parameter of
f could be non-const.  The function f adds nothing to the example.

> The problem is what happens when the second lvalue operand of ?: is
> transformed into the rvalue result of ?:. IMO the standard should be
> clear about whether this transformation is actually conversion and
> should result with new object since it's obviously changes the meaning
> of the program regardless of any optimizations (i.e. eliminations of
> temporaries). I think, and John Potter disagree about it, that the
> (explicit) intention of 5.16/3 is clear and the transformation does
> not introduce new object.

You have concocted yet another example where the error in 5.16/3 does
not apply.

second is string lvalue and third is array of const char lvalue
5.16/3 Different types with at least one class? yes
5.16/3/1 E2 an lvalue? yes for both directions
         neither can be converted to an lvalue of the other type.
5.16/3/2 E2 an rvalue or above failed?  Above failed.
5.16/3/2/1 Both class types? No.
5.16/3/2/2 Conversion possible?  Yes.
       The array is converted to a string temporary rvalue.
5.16/4 Two lvalues of same type?  No.
5.16/5 The result is an rvalue.  Different types?  No.

The conceptual operator selected is
   string operator?(bool, string, string)
5.16/6 Lvalue to rvalue conversion is applied.

The string lvalue is converted to an rvalue if selected.  The array
is converted to a string rvalue if selected.  Because operator?: is
built into the language, there is no function call and no parameter
to initialize.  The conversion is a result of the inlined selection.

> The lvalue is simple changed into rvalue.

This is simply not a possibility.  An lvalue is an address and an rvalue
is a value which has no address.  An rvalue is a temporary for class
types and exists in the vapor for builtins.  In no case does a
modification of an rvalue modify the lvalue source of its existence.

> Now we are back to the "cond ? rvalue : rvalue" case.
> For example:
> struct A {
>     explicit A(int);
>     int val() const;
> private:
>     A(A const&);
> };
>
> extern bool b;
> int  x = (true ? A(10) : A(11)).val();

> EDG, VC, BCC and even GCC found the above code well-formed but it's
> not clear from the standard whether additional "bookkeeping
> simplification" conversion is allowed. IMO in this case compilers will
> make the extra effort to eliminate unnecessarily temporaries due to
> performance concerns (i.e. std::string expensive copying). This also
> shows that GCC makes such optimizations and this is not an
> optimization issue.

It is not an optimization issue.  The pseudo operator function is

   A operator? (bool, A, A)

However, there is no function.  It is a builtin operator which is
inlined in the language.  There is no function call and no requirement
for a copy ctor to make a copy.  It selects the appropriate rvalue.

Replace the rvalue A(10) with an lvalue a and GCC will complain that
it can not perform the lvalue to rvalue conversion because the copy
ctor is private.  Also replace A(11) with an lvalue a2 and it is
again valid because it becomes the operator on lvalues.

Your entire logic is based on an rvalue being something that refers to
the same object as an lvalue.  I find no support for this in the
standard other than the defect in 5.16/3.

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: brok@rubikon.pl (Bronek Kozicki)
Date: Tue, 30 Dec 2003 16:07:58 +0000 (UTC)
Raw View
On Mon, 29 Dec 2003 05:28:01 +0000 (UTC), John Potter wrote:
> On Sun, 28 Dec 2003 03:04:47 +0000 (UTC), brok@rubikon.pl (Bronek
> Kozicki) wrote:
>> Possibly it means that there should be additional note in these few
>> clauses of standard (5.16, 8.5.3) which makes it clear, that creation of
>> this temporary is obligatory. DR, anyone ?
>
> I think that 8.5.3 is quite clear and the compilers are just buggy.

I agree, but when many eyes read it wrong, there's certainly space for
clarification.

> I don't see anything in your issue 434.

issue 434 is about different, albeit related problem, see below.

> B*& -> B const* : without temporary nonsense
>
> This seems to be your concern in 434, but I see nothing to
> allow it in the standard.

you get it wrong; issue 434 is to clearly state that following is
illegal:
B* -> B const *&
D* -> B const *&
... as it requires non-const reference binding to rvalue, while
following:
B* -> B const * const&
D* -> B const * const&
... is allowed, but shall produce temporary, to which const references
are bound, per 8.5.3 "Otherwise, a temporary of type "cv1 T1" is created
and initialized from the initializer expression using the rules for a
nonreference copy initialization (8.5). The reference is then bound to
the temporary.". Possibly this sentence is not clear enough, as many
(most ?) compilers elude this temporary, which changes meaning of valid
C++ program. Anyway, my point is that existence of this temporary is not
within scope of issue 434, thus there's space for another DR.

Kind regards


B.

---
[ 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: rani_sharoni@hotmail.com ("Rani Sharoni")
Date: Wed, 31 Dec 2003 21:31:59 +0000 (UTC)
Raw View
John Potter wrote:
> On Mon, 29 Dec 2003 04:44:13 +0000 (UTC), rani_sharoni@hotmail.com
> (Rani Sharoni) wrote:
>
>> The question is not about optimization that eliminates temporaries.
>
> Actually it is.  There are no non-temporary rvalues.

rvalues and temporaries are different things that are usually confused to be
the same thing.
Rvalue is non-lvalue expression while temporary is object that created is
various contexts.

For example:
int main()
{
     void f();
     f(); // rvalue expression of void type - no temporary is involved
     int x;
     x = 1; // 1 literal is rvalue (constant) expression - no temporary is
involved

     int const& r = 1; // rvalue expression results with temporary
     r; //  lvalue expression that refers to temporary object

     struct A {
          A& self() {
             // lvalue expression that might refer to temporary object
             return *this;
          }
     };
     A().self(); // lvalue expression that refers to temporary object
}

Non-class rvalue is never cv-qualified while non-class temporary might be
const.

Here is interesting quote from DR #177:
"The *temporary is an lvalue* and is used to initialize the variable..."

>> All the implementations I know don't introduce new temporary (of
>> class type) when binding (short-lived) reference to rvalue.
>
> An rvalue is a temporary, no need to introduce a new one.

int f();
int const& r = f(); // return value might be in register and therefore the
standard mandate the introducing of additional const temporary per  8.5.3/5
(non class case).

As we both know even the class type case is allowed to produce additional
*const* temporary.

>> This case is
>> different since the question is whether new object should be
>> introduced when the lvalue operand of the conditional operator
>> becomes an rvalue:
>
> Which it must since by definition, an rvalue is a temporary.

Where did you saw such definition?

>> #include <iostream>
>> #include <string>
>> using namespace std;
>
>> void f(string const&) {}
>
>> int main() {
>>     string str("what");
>>     f( (true ? str : "force rvalue") += " next" ); // #1
>>     cout << str << endl;
>> }
>
>> There are two reference bindings in the above #1 code:
>> 1) When binding the resulting rvalue of ?: to the implicit object
>> parameter in the += call. No additional temporary is allowed
>> (obviously) allowed per 13.3.1/5/1.
>
> Meaningless if there is no rvalue.  An lvalue is not an rvalue and
> may not be used where an rvalue is required.  Producing an rvalue
> from an lvalue produces a temporary.

The conditional operator section (i.e. 5.16) describes iterative process in
which the second and third operands are adjusted to be the same type and
same expression kind. 5.16/3 explicitly mentions cases in which the lvalue
is changed into an rvalue without copying so the standard is clear about
such possibility.
It's not clear whether the "cond ? rvalue : rvalue" case can result with
*additional* temporary that, for example, simplify the destruction
bookkeeping. I didn't encounter any compiler that actually introduce
additional temporary for that case.

>> 2) When binding the f argument to the result of the += lvalue
>> expression. This is usual direct binding per 8.5.3/5
>
> Yes, because the operator returns an lvalue.  The parameter of
> f could be non-const.  The function f adds nothing to the example.

IMO it makes the example a bit more realistic.

>> The problem is what happens when the second lvalue operand of ?: is
>> transformed into the rvalue result of ?:. IMO the standard should be
>> clear about whether this transformation is actually conversion and
>> should result with new object since it's obviously changes the
>> meaning of the program regardless of any optimizations (i.e.
>> eliminations of temporaries). I think, and John Potter disagree
>> about it, that the (explicit) intention of 5.16/3 is clear and the
>> transformation does not introduce new object.
>
> You have concocted yet another example where the error in 5.16/3 does
> not apply.

I think that it does.

> second is string lvalue and third is array of const char lvalue
> 5.16/3 Different types with at least one class? yes
> 5.16/3/1 E2 an lvalue? yes for both directions
>          neither can be converted to an lvalue of the other type.
> 5.16/3/2 E2 an rvalue or above failed?  Above failed.
> 5.16/3/2/1 Both class types? No.
> 5.16/3/2/2 Conversion possible?  Yes.
>        The array is converted to a string temporary rvalue.
> 5.16/4 Two lvalues of same type?  No.
> 5.16/5 The result is an rvalue.  Different types?  No.

I figured that the process is more iterative.
The problem with 5.16/3 is that although it starts with "different types" it
describes cases in which the types are the *same* and this is confusing and
worth clarification.

>> The lvalue is simple changed into rvalue.
>
> This is simply not a possibility.  An lvalue is an address and an
> rvalue is a value which has no address.  An rvalue is a temporary for
> class types and exists in the vapor for builtins.  In no case does a
> modification of an rvalue modify the lvalue source of its existence.

Inside the constructor (or member function) *this which might refer to
temporary is an lvalue. This lvalue refers to an object that later might be
referred by rvalue.
The process of binding *this to rvalue (i.e. A().f()) obviously involves the
address of the rvalue. Nothing new here.
I admit that directly changing lvalue into rvalue as mentioned in 5.16/3 is
quite unique but operator?: is also no of a kind.

>> [...]
> Replace the rvalue A(10) with an lvalue a and GCC will complain that
> it can not perform the lvalue to rvalue conversion because the copy
> ctor is private.  Also replace A(11) with an lvalue a2 and it is
> again valid because it becomes the operator on lvalues.

I wonder what the GCC developer thinks about this issue since I suspect that
they might want to fix this one.

> Your entire logic is based on an rvalue being something that refers to
> the same object as an lvalue.  I find no support for this in the
> standard other than the defect in 5.16/3.

It really easy to doubt me but not EDG and VC that thinks the same.

Rani


---
[ 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                       ]