Lets consider simple reduced example
struct Foo {};
struct Bar : Foo {};
struct Baz : Foo {};
struct X {
operator Bar() { std::cout << "Bar\n"; return {}; }
operator Baz() const { std::cout << "Baz\n"; return {}; }
};
void foo(const Foo &f) {}
int main() {
foo(X{});
}
Here both GCC and clang are printing "Bar", see godbolt example
I am struggling to understand why.
There is no problem to call const-annotated method in rvalue object, see this example
So lets build ICS:
X{} -> user conversion -> Bar -> const Foo&
X{} -> user conversion -> Buz -> const Foo&
Both ICS have 1 user conversion and equivalent standard conversion tail. This shall be ambiguity by [over.ics.rank] C++20.
Can someone help to motivate what happens? I don't believe both mainline compilers agree and wrong here.
foo(X{})
attempts to initialize a parameter of type const Foo&
with a prvalue of type X
.
This falls under [dcl.init.ref]/5.3.2, which directs us to [over.match.ref] to select a conversion function through overload resolution.
Both operator Bar()
and operator Baz() const
are candidates, but the former is a better match because its object parameter is not const
([over.ics.rank]/3.2.6).
So, the (only) conversion sequence from X
to const Foo&
goes through Bar
. And we never rank it against another conversion sequence because there is only one foo
anyway.
An ambiguity would arise in a situation like this:
void foo(Bar);
void foo(Baz);
foo(X());
Now the call is ambiguous because the ICS from X
to Bar
is indistinguishable from X
-> Baz
, despite its first standard conversion sequence being better.