The following reply was made to PR c++/3708; it has been noted by GNATS. From: "Giovanni Bajo" To: "P Hornby" Cc: , , , , Subject: Re: c++/3708: [2003-01-02] static_cast between classes finds ambiguous base conversion Date: Thu, 1 May 2003 03:55:04 +0200 P Hornby wrote: > Long ago I eliminated the template, to get > > class B > { > public: > B() {} > > operator int () { return 1; } > }; > > class D : public B > { > public: > D () {} > > operator Foo () { return Foo(1); }; > }; Which is still ambigous for both GCC and Comeau. > I seem to recall that D's methods are to be preferred over any of B's in > the event of such conflicts. That would be very strange, I never heard of a situation where methods of the most derived class are preferred over members of a less derived class to resolve ambiguity. > I also recall something that implied that > such conflict resolution had a limit on the complexity, and that this > limit was greater than a single direct call to a method of a class. I am not sure about what you mean here. > forget. In any case, the template version of the conflict is excluded on > the grounds that the D::operator Foo() -> Foo::Foo(const Foo&) requires > no template instantiation. (under the rule that the less instantiated > resolution yields to the more instantiated (in this case explict) > resolution). I think you're slightly missing the point. There is no ambiguity between operator Foo() and template operator T(). Let's go step by step. The compiler needs to convert "d" (of type D) to type Foo. Since Foo() has a constructor taking a parameter of type "int", the compiler knows that the conversion can be done directly (finding an user-defined conversion to Foo, and calling the copy constructor) or indirectly (finding an user-defined conversions to int and calling the custom constructor). In the standard, there is nothing on why a constructor might be preferred over the other, so the conversion to type Foo() is not "better" in any way to the conversion to type int, since both have an accessible constructor in Foo (the implicit copy constructor for the former, the explicit custom constructor for the latter). So, the compiler just found out it can try to convert to either Foo or int. It checks for custom conversion functions, and it finds two of them: a conversion to Foo, and a template conversion operator. One is in B and one is in D, but §12.3p5 says "[...]A conversion function in a derived class does not hide a conversion function in a base class unless the two functions convert to the same type[...]". So both of them are visible. First, let's say that the compiler tries to convert to int. The non-template operator obviously cannot be used, but the template operator can be instanciated with type int. So the compiler now know that there is an available conversion from D to int, through the template operator in B. Second, it tries to see if it's possible to convert Foo to int. In this case, the compiler finds that there is a non-template operator Foo() which can be used, but also a template operator that could be instanciated with type Foo. So what? Again §12.3p5: "Function overload resolution selects the best conversion function to perform the conversion". Thus, the compiler prefers the non-template version over the template one (for the usual overaload rules). As you see, the compiler ends up with two valid paths to perform the required conversion. One path goes through conversion to int (thanks to the custom constructor) and one path through conversion to Foo (thanks to the implicit copy constructor). > This would be the case if the conflict were to arise from a > D::operator T(), so why break the rule for B::operator T()? (I know why > the compiler is doing this, and it is very hard to fix. I understand why > you are keen to close it). No, it's wrong. Even if you move the template operator into D, there is still an ambiguity. In fact (again) the ambiguty is not about choosing between the non-template conversion operator and the template one. Remember that a group of conversion functions do not form an overload set. You seem to think as your code is in the same situation of: template void foo(T a); void foo(int a); where the second overload is obviously preferred to the first if the function is called with a varible of type int. But here, the situation is different. There is no overload set. The ambiguity is between which constructor must be called! Both of them are accessible, both of them are reachable through custom conversion functions (which are not overloaded! they are different functions!), so there is no way to solve this ambiguity. To solve this as you think, there should be an explicit paragraph in the standard stating that, in case of ambiguity during a conversion, a conversion made through a non-template operator should be preferred over a conversion made through a template operator. But there is no such a rule. > As I've stated elsewhere g++ (and now Comeau) are the only compilers I > have encountered that do not resolve this conflict. I only ask that you > consider this once more before closing it. (You may run into a language > lawyer next time). Sure, there is no hurry. In case nobody helps us here, I'll bring the issue to comp.std.c++ soon. Giovanni Bajo