MSVC is correct according to the current draft standard, but there is an ongoing CWG issue that will instead specify the other compilers' behavior. (Spoiler: it's CWG 1827.)
For const A& a = b;, overload resolution is performed twice.
First, per [dcl.init.ref]/(5.1):
A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:
If the reference is an lvalue reference and the initializer expression
- [...]
- has a class type (i.e.,
T2 is a class type), where T1 is not reference-related to T2, and can be converted to an lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (this conversion is selected by enumerating the applicable conversion functions ([over.match.ref]) and choosing the best one through overload resolution),
then the reference binds to the initializer expression lvalue in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).
Here, overload resolution only considers conversion functions that return an lvalue reference.
All compilers agree that b cannot be converted to an lvalue of type const A (or a reference-compatible type) due to the user-defined conversion being ambiguous, but only MSVC proceeds to the next bullets, while other compilers stop with an error here.
The next bullets are
- Otherwise, if the reference is an lvalue reference to a type that is not const-qualified or is volatile-qualified, the program is ill-formed.
const A& is an lvalue reference to a const-qualified type, so we can continue.
Otherwise, if the initializer expression
- [...]
- has a class type (i.e.,
T2 is a class type), where T1 is not reference-related to T2, and can be converted to an rvalue of type “cv3 T3” or an lvalue of function type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (see [over.match.ref]),
then the initializer expression in the first case and the converted expression in the second case is called the converted initializer. [...] In any case, the reference binds to the resulting glvalue (or to an appropriate base class subobject).
For this second overload resolution, b can be converted to an rvalue of type const A using the operator A&& conversion function. So according to the standard, b binds to the result of said conversion function.
Only MSVC implements this to the letter.
But the other compilers' behavior is arguably more intuitive, and as mentioned in CWG 1827, in 2014, the working group agreed that such initialization should be made ill-formed.
CWG agreed that an ambiguity like this should make the initialization ill-formed instead of falling through to do indirect binding.
However, this issue is still in "drafting" status, which indicates that it's not formally a defect report due to a lack of wording.