khuttun
khuttun

Reputation: 733

Does C++ guarantee this enum vs int constructor overload resolution?

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?

Upvotes: 4

Views: 202

Answers (2)

songyuanyao
songyuanyao

Reputation: 172924

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:

  1. 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
  2. Promotion: integral promotion, floating-point promotion
  3. 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

A standard conversion sequence S1 is better than a standard conversion sequence S2 if

a) S1 is a subsequence of S2, excluding lvalue transformations. The identity conversion sequence is considered a subsequence of any other conversion

b) Or, if not that, the rank of S1 is better than the rank of S2

F1 is determined to be a better function than F2 if implicit conversions for all arguments of F1 are not worse than the implicit conversions for all arguments of F2, and

  1. there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2

These pair-wise comparisons are applied to all viable functions. If exactly one viable function is better than all others, overload resolution succeeds and this function is called. Otherwise, compilation fails.

Upvotes: 5

Quimby
Quimby

Reputation: 19123

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 call S(int) as a fallback because MyEnum is implicitly convertible to integers. But this is worse overload candidate than S(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 call S(MyEnum), although it limits some implicit conversion, it does not impact this example and S(int) would be called had S(MyEnum) was removed.
  • return S(A); is essentially the same as return A; given the specified return type S.

Upvotes: 4

Related Questions