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?