Topic: Defect Report: Can an rvalue refer to an lvalue's object?
Author: John Potter <jpotter@falcon.lhup.edu>
Date: Tue, 06 Jan 2004 14:54:41 GMT Raw View
[Moderator's note: this defect report has been
forwarded to the C++ committee. -moderator.]
5.16 Conditional Operator [expr.cond]
The problem occurs when the value of the operator is determined to
be an rvalue, the selected argument is an lvalue, the type is a class
type and a non-const member is invoked on the modifiable rvalue result.
struct B {
int v;
B (int v) : v(v) { }
void inc () { ++ v; }
};
struct D : B {
D (int v) : B(v) { }
};
B b1(42);
(0 ? B(13) : b1).inc();
assert(b1.v == 42);
The types of the second and third operands are the same and one is an
rvalue. Nothing changes until p6 where an lvalue to rvalue conversion
is performed on the third operand. 8.2 states that an lvalue to rvalue
conversion produces a temporary and there is nothing to remove it. It
seems clear that the assertion must pass, yet most implementations fail.
There seems to be a defect in p3 b2 b1. First, the conditions to get
here and pass the test.
If E1 and E2 have class type, and the underlying class types are the
same or one is a base class of the other: E1 can be converted to
match E2 if the class of T2 is the same type as, or a base class of,
the class of T1, and the cv-qualification of T2 is the same
cv-qualification as, or a greater cv-qualification than, the
cv-qualification of T1.
If both E1 and E2 are lvalues, passing the conditions here also passes
the conditions for p3 b1. Thus, at least one is an rvalue. The case of
two rvalues is not interesting and the action is covered by the case
when E1 is an rvalue.
(0 ? D(13) : b1).inc();
assert(b1.v == 42);
E1 is changed to an rvalue of type T2 that still refers to the
original source class object (or the appropriate subobject thereof).
[Note: that is, no copy is made. ]
Having changed the rvalue to base type, we are back to the above case
where an lvalue to rvalue conversion is required on the third operand
at p6. Again, most implementations fail.
The remaining case, E1 an lvalue and E2 an rvalue, is the defect.
D d1(42);
(0 ? B(13) : d1).inc();
assert(d1.v == 42);
The above quote states that an lvalue of type T1 is changed to an rvalue
of type T2 without creating a temporary. This is in contradiction to
everything else in the standard about lvalue to rvalue conversions.
Most implementations pass in spite of the defect.
The usual accessible and unambiguous is missing from the base class.
There seems to be two possible solutions. Following other temporary
creations would produce a temporary rvalue of type T1 and change it
to an rvalue of type T2. Keeping the no copy aspect of this bullet
intact would change the lvalue of type T1 to an lvalue of type T2.
In this case the lvalue to rvalue conversion would happen in p6 as
usual.
Suggested wording for p3 b2 b1
The base part:
If E1 and E2 have class type, and the underlying class types are the
same or one is a base class of the other: E1 can be converted to match
E2 if the class of T2 is the same type as, or an accessible and
unambiguous base class of, the class of T1, and the cv-qualification
of T2 is the same cv-qualification as, or a greater cv-qualification
than, the cv-qualification of T1. If the conversion is applied:
The same type temporary version:
If E1 is an lvalue, an lvalue to rvalue conversion is applied. The
resulting or original rvalue is changed to an rvalue of type T2 that
refers to the same class object (or the appropriate subobject
thereof). [Note: that is, no copy is made in changing the type of
the rvalue. ]
The never copy version:
The lvalue(rvalue) E1 is changed to an lvalue(rvalue) of type T2
that refers to the original class object (or the appropriate
subobject thereof). [Note: that is, no copy is made. ]
The test case was posted to clc++m and results for implementations
were reported.
#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
I see no defect with regards to lvalue to rvalue conversions; however,
there seems to be disagreement about what it means by implementers.
It may not be surprising because 5.16 and passing a POD struct to an
ellipsis are the only places where an lvalue to rvalue conversion
applies to a class type. Most lvalue to rvalue conversions are on
basic types as operands of builtin operators.
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 ]