*This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21
Core Issues List revision 112c.
See http://www.open-std.org/jtc1/sc22/wg21/ for the official
list.*

2023-11-10

[Voted into WP at March 2004 meeting.]

During a discussion over at the boost mailing list (www.boost.org), we came across the following "puzzle":

struct A { template< typename T > operator T() const; } a; template<> A::operator float() const { return 1.0f; } int main() { float f = 1.0f * a; }

The code is compiled without errors or warnings from EDG-based compilers (Comeau, Intel), but rejected from others (GCC, MSVC [7.1]). The question: Who is correct? Where should I file the bug report?

To explain the problem: The EDG seems to see 1.0f*a as a call to the unambiguous operator*(float,float) and thus casts 'a' to 'float'. The other compilers have several operators (float*float, float*double, float*int, ...) available and thus can't decide which cast is appropriate. I think the latter is the correct behaviour, but I'd like to hear some comments from the language lawyers about the standard's point of view on this problem.

__Andreas Hommel:__
Our compiler also rejects this code:

Error : function call 'operator*(float, {lval} A)' is ambiguous 'operator*(float, unsigned long long)' 'operator*(float, int)' 'operator*(float, unsigned int)' 'operator*(float, long)' 'operator*(float, unsigned long)' 'operator*(float, float)' 'operator*(float, double)' 'operator*(float, long double)' 'operator*(float, long long)' Test.cp line 12 float f = 1.0f * a;

Is this example really legal? It was my understanding that all candidates from 12.5 [over.built] participate in overload resolution.

__Daveed Vandevoorde:__
I believe the EDG-based compiler is right. Note that the built-in operator*
requires "usual arithmetic conversions" (see
7.6.5 [expr.mul] paragraph 2 and
Clause 7 [expr] paragaph 9). This means that
there is no candidate taking (float, double) arguments: Only (float, float)
or
(double, double).

Since your first argument is of type float, the (float, float) case is preferred over the (double, double) case (the latter would require a floating-point promotion).

__Stave Adamczyk:__
Daveed's statement is wrong; as Andreas says, the prototypes in
12.5 [over.built] paragraph 12
have pairs of types, not the same type twice. However, the list of
possibilities considered in Andreas' message is wrong also:
12.5 [over.built] paragraph 12
calls for pairs of **promoted** arithmetic types, and float is not
a promoted type (it promotes to double -- see
7.3.8 [conv.fpprom]).

Nevertheless, the example is ambiguous. Let's look at the overload resolution costs. The right operand is always going to have a user-defined-conversion cost (the template conversion function will convert directly to the const version of the second parameter of the prototype). The left operand is always going to have a promotion (float --> double) or a standard conversion (anything else). So the cases with promotions are better than the others. However, there are several of those cases, with second parameters of type int, unsigned int, long, unsigned long, double, and long double, and all of those are equally good. Therefore the example is ambiguous.

Here's a reduced version that should be equivalent:

struct A { template <typename T> operator T() const; } a; void f(double, int); void f(double, unsigned int); void f(double, long); void f(double, unsigned long); void f(double, double); void f(double, long double); int main() { f(1.0f, a); // Ambiguous }

Personally, I think this is evidence that 12.5 [over.built] doesn't really do quite what it should. But the standard is clear, if possibly flawed.

__Andreas Hommel:__
You are right, "float" is not a promoted arithmetic type, this is a bug in
our compiler.

However, the usual arithmetic conversions (Clause 7 [expr] paragraph 9) do not promote the floating point types, so

float operator+(float, float);is a legal built-in operator function, so I wonder if it shouldn't be included in the candidate list.

__Steve Adamczyk:__ Hmm, the definition of the term in
12.5 [over.built] paragraph 2 is highly ambiguous:

Similarly, the term promoted arithmetic type refers to promoted integral types plus floating types.I can't tell if that's "promoted integral types plus (all) floating types" or "promoted integral types plus (promoted) floating types". I thought the latter was intended, but indeed the usual arithmetic conversions could give you "float + float", so it makes sense that float would be one of the possibilities. We should discuss this to make sure everyone has the same interpretation.

**Proposed Resolution (October 2003):**

Change the second sentence of 13.6 paragraph 2 as follows:

Similarly, the termpromoted arithmetic typerefers to~~promoted integral types plus floating types~~floating types plus promoted integral types.