Re: overload resolution and conversion ops
 
* James Kanze:
On 16 mai, 14:24, xtrigger...@gmail.com wrote:
I was reading Mr. Alexandrescu's mojo article and I've a hard
time understanding the following.
Let's suppose I have:
//code
struct A {};
struct B : A {};
struct C
{
    operator A() const { return A(); }
    operator B() { return B(); }
};
void F( A const & ) {}
void F( B const & ) {}
C const H() { return C(); }
int main()
{
    F( C() );               // 1. called with a non-const rvalue
    F( H() );               // 2. called with a const rvalue}
}
//end code
I understand that if I have a const "C" (rvalue or lvalue,
here it does not matter) the only option when calling "F"
would be to use
C::operator A() const -> F( A const & )
Correct.  The C::operator B() conversion operator cannot be
called (is not a viable function, in the words of the standard).
Agreed.
In the case I have a non-const "C" it seems that the compiler
chooses
C::operator B() -> F( B const & )
my understanding of the article is that this is because since
B is derived from A, it's a better match than the base class.
I do not understand then why the constness of operator A()
makes the difference.
It's rather the non-const-ness of operator B() that makes the
difference.  Because the operator is not const, it cannot be
called on a const object.
But here you have overlooked the call 'F( C() )'.
Remove the call 'F(H())' and the const on op B still makes a 
difference... :-)
OK, I don't understand this, even after scrutinizing the standard, but 
I recognize that your explanation isn't it, that it ignores the really 
relevant aspects.  I should understand it, after all it's basic!, and 
I think I did understand it once, as one of the volunteers helping 
Andrei with quality assurance on Mojo.  But darned if I now can get my 
head around this (same predicament as the OP)!
If I remove it and make it just
C::operator A()
my guess would be that that when calling "F" I would always
get a call to
F( B const & ). But instead the thing does not compile.
If the object is const, only const functions can be called on
it.  If you remove const everywhere, then no functions can be
called on the object.  Whence your error.
Again, here you have overlooked the call 'F( C() )'.
Here's a FLAWED analysis, with some facts.  Would appreciate it if you 
could return my help in pointing out flaw in your above explanation, 
by pointing out flaw(s) in this... :-) :-)  The facts seem to stand.
The call
   F( C() )
can be resolved as
   F( C().operator A() )         // calls F( A const& )
or
   F( C().operator B() )         // calls F( B const& )
Data point matrix (apparently these are facts):
   case   const on op A    const on op B    result
     0      no                no              Ambigious, F(A) or F(B)
     1      no               YES              Ambigious, F(A) or F(B)
     2     YES                no              F(B)
     3     YES               YES              Ambigious, F(A) or F(B)
The most interesting case seems to be case 1.  Here, from na?ve point 
of view, the class derivation relationship wants F(B), most specific, 
but the extra const "conversion" wants F(A), fewest "conversions". 
However, let's first consider cases 0 and 3, where op A() and op B() 
compete directly, on equal footing, so to speak.
Hm, standard, what do you say?
OK, ?13.3.3.2/2 is the top-level of how to rank implicit conversion 
sequences, paraphrased:
    Best:    Standard conversion sequence
    So-so:   User defined conversion sequence
    Worst:   Ellipsis conversion sequence (a "..." formal argument)
What we have is two user defined conversion sequences to be compared 
(from C to A or B result), so that top level ranking doesn't come into 
play directly, but it can affect rankings for sub-sequences.  For a 
user-defined conversion sequence is defined by ?13.3.3.1.2 to consist 
of a standard conversion sequence followed by a (single) user defined 
conversion followed by a standard conversion sequence.  Sort of like 
pre-conversion adjustment, real conversion, and post fixups.
   Case 0
   Seq    Pre           Real (user)    Post               Calls
    s1    identity       op A()        A -> A const&      F(A const&)
    s2    identity       op B()        B -> A const&      F(A const&)
    s3    identity       op B()        B -> B const&      F(B const&)
s2 is ranked lower than s3 because the post-conversion of s3 (recall, 
the post conversion is a standard conversion sequence, not involving 
any calls) is more specific, at least according to my reading of 
?13.3.3.2/4.  So that leaves s1 and s3 to choose from.
But s1 and s3 involve the same kinds of conversions.
We do not even need to know the exact rules.  The specificity of the 
result of s3 does not enter into the ranking because that specificity 
is not of a standard conversion sequence: specificity of class affects 
only ranking of standard conversion (like, you have B, then a B 
result, doing nothing, is better than direct "upcast" to A, at least 
according to my reading of ?13.3.3.2/4).  So in this case s1 and s3, 
as I understand it, ends up with same rank, and the call is ambigious.
*A key notion*: that "closeness" in inheritance only affects ranking 
of standard conversions, not ranking of a user-defined conversion.
Analysis for case 3 would be the same, so, case 1 then:
   Case 1
   Seq    Pre           Real (user)    Post               Calls
    s1    identity       op A()        A -> A const&      F(A const&)
    s2    C -> C const   op B()        B -> A const&      F(A const&)
    s3    C -> C const   op B()        B -> B const&      F(B const&)
Here again s2 is ranked lower than s3.  But it would seem that s1 
should prevail, should be ranked higher than s3, because of the const 
conversion in the pre-conversion.  However, ?13.3.3.2/3 tells us that
   "User-defined conversion sequence U1 is a better conversion sequence
   than another user-defined conversion sequence U2 if they contain the
   same user-defined conversion function or constructor and if the
   second standard conversion sequence [the "Post" above] of U1 is
   better than the second standard conversion sequence of U2."
And it seems there is no other criterion for top-level ranking of user 
defined conversion sequences.
Hence, in order to for s1 to be better than s3 it would have to 
involve the same conversion operator, and it would have to be better 
in the post-conversion.  The pre-conversion is simply ignored, wrt. 
ranking.  The result is that s1 and s3 are ranked equally, ambigious.
Finally, analysis for case 2:
   Case 2
   Seq    Pre           Real (user)    Post               Calls
    s1    C -> C const   op A()        A -> A const&      F(A const&)
    s2    identity       op B()        B -> A const&      F(A const&)
    s3    identity       op B()        B -> B const&      F(B const&)
But this analysis fails, in the sense that it tells us that since s1 
and s3 involve different user defined conversion functions, neither 
one is ranked better than the other.  So according to this analysis 
they "should" be equally ranked, ambigious.  But they're not.
Cheers,
- Alf (perplexed, perhaps needs to read Andrei's article again!)
-- 
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?