Topic: Defect Report: Conditional operator inconsistency


Author: wasti.redl@gmx.net
Date: Mon, 13 Apr 2009 12:59:48 CST
Raw View
Hi,

Consider the following snippet:

-------------------------

struct D;
struct B { operator D&(); };
struct D : B {};

void fn()
{
   B b;
   D d;
   coinflip() ? B() : D(); // #1
   coinflip() ? b : d;  // #2
}

-------------------------

Curiously enough, expression #1 is valid and an rvalue of type B,
while expression #2 is ambiguous and ill-formed.
C++ 5.16p3 says that the operands are attempted to be converted to
each other, and if exactly one direction succeeds, this conversion is
used. If both succeed, the conversion is ambiguous and the program is
ill-formed.

Looking at the conversion attempts in order, we get the following
results:

#1, converting B() to D(): Bullet 1 requires lvalues, and doesn't
apply. Bullet 2 applies.
Bullet 2.1 requires that either class is base of the other, which is
true; B is a base of D. In that case, derived-to-base can be
performed. However, this fails here, since we're attempting to convert
B to D.
Bullet 2.2 doesn't apply, because 2.1 applied (even though it failed).

#1, converting D() to B(). Same conditions as previously, except that
the derived-to-base conversion D->B is valid. If the expression D() is
chosen, a temporary B() will be constructed from the D() using B's
copy constructor.

#1 has exactly one valid conversion, so this conversion is used and
the line is well-formed.

#2, converting b to d. Bullet 1 requires lvalues, which we have. It
then looks for an implicit conversion that allows direct binding of a
D& to the value, which succeeds through the user-defined conversion in
B.
Bullet 2 doesn't apply.

#2, converting d to b. Bullet 1 applies again. The direct binding of a
B& to d succeeds because B is a base of D.

#2 has two valid conversions, so it's ambiguous and ill-formed.

Comeau Online agrees with my interpretation and flags #2 as an error,
while letting #1 pass.

This strikes me as inconsistent. Basically, it allows a wider search
for conversions for lvalues than for rvalues, and I see no reason that
this should be so. I think the phrase "one is a base class of the
other" in bullet 2.1 should be changed to "the class of T2 is a base
of the class of T1" or something to that effect. This would lead to
bullet 2.2 applying to the B() -> D() conversion, which would discover
the user-defined conversion, and thus make #1 ambiguous, consistent
with #2.
Alternatively, or in addition, if both directions are valid
conversions, if one conversion is better than the other according to
the rules of overload resolution, this conversion shall be the one
used. In this case, binding a B& to d would be a better conversion
(derived-to-base) than binding a D& to b (user-defined), and thus make
#2 valid (the entire expression being an lvalue of type B), again
reaching consistency. (#1 would be valid even if this change is in
addition to the first, because here too derived-to-base would be a
better conversion than user-defined.)

Regards,
Sebastian

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Faisal Vali <faisalv@gmail.com>
Date: Wed, 15 Apr 2009 14:50:35 CST
Raw View
On Apr 13, 1:59 pm, wasti.r...@gmx.net wrote:
<snip>
> struct D;
> struct B { operator D&(); };
> struct D : B {};
>
> void fn()
> {
>    B b;
>    D d;
>    coinflip() ? B() : D(); // #1
>    coinflip() ? b : d;  // #2
>
> }
>
> -------------------------
>
> Curiously enough, expression #1 is valid and an rvalue of type B,
> while expression #2 is ambiguous and ill-formed.

<snip>
> Comeau Online agrees with my interpretation and flags #2 as an error,
> while letting #1 pass.
>
> This strikes me as inconsistent.
<snip>

I agree with you here that there is an inconsistency.  For example if
you add a direct rvalue conversion to B:
i.e. operator D(); the rvalue case is still not flagged as ambiguous.
I don't feel like strongly that the above code as you had originally
written it should compile any differently, but once you add an rvalue
conversion (operator D() or operator D&&()) I feel it should make both
cases ambiguous.

And I would agree that the fix might be along the lines you suggest -
instead of saying if either is a base of the other, the standard
should read if T2 is a base of T1.

Can anyone think of a good reason that the original code as written
should be ill-formed even if both expressions are rvalues (i.e the
lvalue conversion operator D&() should be considered and then its
result converted to an rvalue?)

regards,
Faisal Vali
Radiation Oncology
Loyola




--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]