Consider this example program:
#include <iostream>
typedef enum { A, B, C } MyEnum;
struct S
{
S(int) { std::cout << "int" << std::endl; }
S(MyEnum) { std::cout << "MyEnum" << std::endl; }
};
S f()
{
return A;
}
int main()
{
S const s = f();
}
Compiled with both clang and gcc this produces an executable that prints "MyEnum" when run. Is this behavior guaranteed by the C standard?
CodePudding user response:
Yes, S::S(MyEnum)
wins in overload resolution because it's an exact match. While S::S(int)
requires one more implicit conversion (integral promotion) from enum to int
.
Each type of standard conversion sequence is assigned one of three ranks:
- Exact match: no conversion required, lvalue-to-rvalue conversion, qualification > conversion, function pointer conversion, (since C 17) user-defined conversion of class type to the same class
- Promotion: integral promotion, floating-point promotion
- Conversion: integral conversion, floating-point conversion, floating-integral conversion, pointer conversion, pointer-to-member conversion, boolean conversion, user-defined conversion of a derived class to its base
CodePudding user response:
Yes, of course. return
statement allows implicit construction and S(MyEnum)
is the exact match.
Same would work with return {A};
But if you were to make S(MyEnum)
explicit, then:
return A;
will callS(int)
as a fallback becauseMyEnum
is implicitly convertible to integers. But this is worse overload candidate thanS(MyEnum)
due to the extra conversion, chosen only from necessity.return {A};
represents copy-list initialization. It will fail because it forbids explicit constructors and implicit conversions.return S{A};
represents direct-list initialization, it will callS(MyEnum)
, although it limits some implicit conversion, it does not impact this example andS(int)
would be called hadS(MyEnum)
was removed.return S(A);
is essentially the same asreturn A;
given the specified return typeS
.