For your first example, the parameter of the first function is of type `int` and the corresponding argument has type `S`, Hence, the following rules apply here:
> Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion functions are enumerated (
[over.match.conv]), and the best one is chosen through overload resolution.
Through look at the rule in [over.match.conv], the candidate functions are formed by:
> The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and
yield type T or a type that can be converted to type T via a standard conversion sequence are candidate functions.
Hence, `operator int() const &` and `operator char() &&` are all candidate functions, in order
to determine which is the best, hence the overload resolution applies here to find out the best, in the first candidate function, the implicit parameter object type is `S const&`. Rather, the implicit parameter object type of the second candidate is `S&&`.
In this case, the argument is of type `S` and a prvalue. According to:
[over.ics.rank#3.2.3]
>S1 and S2 are reference bindings ([dcl.init.ref]) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.
`operator char() &&` wins the game.
And according to:
>The effect of any implicit conversion is the same as performing the corresponding declaration and initialization and then using the temporary variable as the result of the conversion. The result is an lvalue if T is an lvalue reference type or an rvalue
reference to function type ([dcl.ref]), an xvalue if T is an rvalue reference to object type, and a prvalue otherwise. The expression e is used as a glvalue if and only if the initialization uses it as a glvalue.
That means `char tmp = S{};` where the `tmp` is used as the argument in function call. A similar process undergoes for `void foo(char)`. Now, the first example can be simplified
to when the argument is of type `char`, which function is the best when calling foo(tmp). Certainly, it is `void foo(char)`.
Now, let us consider the second example.
Arguably, it is different from the first example. the parameter of `
void foo(const int &)` is of type `int const&`. So [
dcl.init.ref]
applies here. According to
[dcl.init.ref#5.1.2] and
[over.match.ref#1.1], all functions that have return
type of lvalue reference to type T are candidate functions, here only `
operator const int &() const &` is. And from the type of this conversion to `const int &` has an identity
conversion. Instead, for the second `
void foo(int &&)`, the candidate conversion function is only `
operator
int &&() &&` per
[dcl.init.ref#5.2.1.2] and [over.match.ref#1.1]. From the result of `operator int &&() &&` to `int &&` is also an identity conversion. Hence, your second example
gives an ambiguous error.