7

When I try to compile (with gcc 4.3.4) this code snippet:

enum SimpleEnum {
    ONEVALUE
};

void myFunc(int a) {
}

void myFunc(char ch) {
}

struct MyClass {
    operator int() const { return 0; };
    operator SimpleEnum() const { return ONEVALUE; };
};

int main(int argc, char* argv[]) {
    myFunc(MyClass());
}

I get this error:

test.cc: In function "int main(int, char**)":
test.cc:17: error: call of overloaded "myFunc(MyClass)" is ambiguous
test.cc:5: note: candidates are: void myFunc(int)
test.cc:8: note:                 void myFunc(char)

I think I (almost) understand what the problem is, i.e. (simplifying it a lot) even if I speak about "char" and "enum", they all are integers and then the overloading is ambiguous.

Anyway, the thing I don't really understand is that if I remove the second overloading of myFunc OR one of the conversion operators of MyClass, I have no compilation errors.

Since I'm going to change A LOT of old code because of this problem (I'm porting code from an old version of HP-UX aCC to g++ 4.3.4 under Linux), I would like to understand better the whole thing in order to choose the best way to modify the code.

Thank you in advance for any help.

Kilian Foth
  • 13,904
  • 5
  • 39
  • 57
  • Does it work for you if you remove the conversion to int? Or is it still ambiguous conversion from enum to int/char like others are suggesting? – K-ballo Sep 15 '11 at 16:24
  • @K-ballo: If I remove one of the two conversions, it works. If I remove "void myFunc(char ch)", it works. If I remove "void myFunc(int a)", I get (almost) the same error. – portingportingporting Sep 16 '11 at 07:03
  • I'm stick to thinking that having the above sample not compiling and the one without the "void myFunc(char ch)" compiling, it's plainly wrong from a common sense point of view and, for sure, counterintuitive. Anyway, from your answers, I understand that this is the (ugly) standard and I will respect it (also because I have no alternatives). Just for information, I have decided to put explicit casts to int into my production code. Thank you to anyone that answered and, hence, helped me to understand better the whole thing. – portingportingporting Sep 19 '11 at 10:36

3 Answers3

4

enums are types in C++, unlike C.

There are implicit conversions for both enum -> char and enum -> int. The compiler just doesn't know which one to choose.


EDIT: After trying with different tests:

  • When the definition for custom conversion MyClass -> int is removed, code compiles.

  • Here there is implicit conversion for enum to int and so it is the one favored by the compiler over the one to char. Test here.

  • When the definition for void myFunc(int) is removed compilation fails.

  • Compiler tries to convert from MyClass to char and finds that, not having a user defined conversion operator char(), both user defined int() and SimpleEnum() may be used. Test here.

  • When you add a char() conversion operator for MyClass compilation fails with the same error as if not.

  • Test here.

So the conclusion I come up with here is that in your originally posted code compiler has to decide which of the two overloaded versions of myFunc should be called. Since both conversions are possible:

  1. MyClass to int via user defined conversion operator.
  2. MyClass to int via user defined conversion (MyClass to SimpleEnum) + implicit conversion (SimpleEnum to char)

compiler knows not which one to use.

Antonio Pérez
  • 6,702
  • 4
  • 36
  • 61
  • The ambiguity is not between `enum` and `int` or `char`, its actually between `enum` which is convertible to `int`, and the user defined conversion to `int`. A call yo `myFunc( ONEVALUE )` should be ok, and the int overload would be chosed. – K-ballo Sep 15 '11 at 19:59
  • Then why does the compiler yield a misleading error description? – Antonio Pérez Sep 15 '11 at 21:21
  • @K-ballo: it looks like it is a combination of both things, check this out: http://ideone.com/75Huq – Antonio Pérez Sep 15 '11 at 21:30
  • Indeed, if the only overload is the int one then the user defined conversion to int is the 'shorter' path and thus is chosen. I still think that this code should not result in a compiler error, as the user defined conversion to int should be picked in every case... I'm hoping for a standard guru to tell us whether the code is legal or not. – K-ballo Sep 15 '11 at 21:32
  • @K-ballo: in effect I was hoping in that shorter path being chosen (and that happened with old aCC compiler because it was not up to date with the current C++ standard) – portingportingporting Sep 16 '11 at 07:20
4

The conversion from MyClass is ambiguous, as there is one conversion to int and one to the enum, which is itself implicitly convertible to int, and both are equally good conversions. You can just make the call explicit, though, by specifying which conversion you want:

myfunc(int(MyClass()));

Alternatively, you might like to rethink why you have a function that has separate overloads for int and char, perhaps that could be redesigned as well.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • The ambiguity is not between `enum` and `int` or `char`, its actually between `enum` which is convertible to `int`, and the user defined conversion to `int`. A call yo `myFunc( ONEVALUE )` should be ok, and the int overload would be chosed. – K-ballo Sep 15 '11 at 19:59
  • @K-Ballo: You're right, I messed that one up. Let me fix it. Thanks! – Kerrek SB Sep 15 '11 at 20:13
  • The fact that the two conversion operators could be "conflicting", was immediately clear to me and the fact that I could solve this issue by explicitly specifying which conversion to use, was verified before my post here. Anyway what mostly disturbs me is that if I remove the "char" overload of myFunc, everything works fine. But the two conversions of MyClass are yet there, why then, I get error no more? I hope understanding this fact can help me in finding a suitable solution (explicit conversion would cause a major rework of code and, hence, I'm trying to avoid it). – portingportingporting Sep 16 '11 at 07:11
  • @port: If there' is only one single version of `myfunc`, then the conversion to `int` is better because it requires *no* implicit conversion, so there's no ambiguity. – Kerrek SB Sep 16 '11 at 10:48
1

I would have expected that the int overload is called. Tried a few compilers and got different results. If you are going to remove anything, remove the user conversion operator to int, since enums have a standard conversion to ints.

K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • Unfortunately the real case is a bit more complex than the sample I posted: I have pseudo-enum classes that can be converted to int in order to be stored to DB and that can be converted to other enums that are CORBA enums in order to communicate with other processes. I really can't remove any conversion (or I can and then I have to manually fix thousands of lines of codes searching them within hundreds of thousands of lines of codes). – portingportingporting Sep 16 '11 at 07:15