Topic: Overloading Resolution
Author: hitz@csi.uottawa.ca (Martin Hitz)
Date: 26 Jun 91 14:05:08 GMT Raw View
In the course of testing my understanding of the rules for argument matching
given in section 13.2 in the ARM, I tried to predict the outcome of the
program given below. In order to verify my conclusions, I tried the
program with 4 different compilers (GNU g++, Sun's cfront, Zortech, and
Borland C++). As the results have been somewhat arbitrary, I'd like to
get your comments on who's right:
#include <stream.h>
void f (const double &, double, void *) { cout << 1; }
void f (double &, int, const char * = 0) { cout << 2; }
void f (int) { cout << 3; }
void f (long, int = 0) { cout << 4; }
main ()
{
double b;
const double pi = 3.14;
// Z g++ cfront BC++
//---------------------------------------
f(1.1, 1, f); // 1 err err 1
f(1, 1, "Y"); // err err err 2
f(3, 1.1, 0); // err err 1 err
f(1.1, 3, (void*)0);// err 1 1 err
f(1, 1); // err 4 4 4
f(pi, pi, "Y"); // 1 1 1 err
f(0L); // 4 3 4 4
f(0L, 'a'); // err 4 4 4
f(pi); // err 3 err err
}
Thanks, Martin Hitz@csi.uottawa.ca
Author: jangr@microsoft.UUCP (Jan GRAY)
Date: 4 Jul 91 02:56:48 GMT Raw View
In article <1991Jun26.140508.19662@csi.uottawa.ca> hitz@csi.uottawa.ca (Martin Hitz) writes:
>In the course of testing my understanding of the rules for argument matching
>given in section 13.2 in the ARM, I tried to predict the outcome of the
>program given below. In order to verify my conclusions, I tried the
>program with 4 different compilers (GNU g++, Sun's cfront, Zortech, and
>Borland C++). As the results have been somewhat arbitrary, I'd like to
>get your comments on who's right:
OK. Here goes...
>#include <stream.h>
>
>void f (const double &, double, void *) { cout << 1; }
>void f (double &, int, const char * = 0) { cout << 2; }
>void f (int) { cout << 3; }
>void f (long, int = 0) { cout << 4; }
>
>main ()
>{
> double b;
> const double pi = 3.14;
>
> // Z g++ cfront BC++
> //---------------------------------------
> f(1.1, 1, f); // 1 err err 1
Should be error.
Because there are three actuals, only f[1] and f[2] could be candidates.
However, there is no conversion from the actual expression "f" (overloaded)
to either of "void *" or "const char *".
13.2: "...A call of a given function name chooses, from among all functions
by that name that are in scope and for which a set of conversions exists so
that the function could possibly be called ..."
For this set of actuals, no function could possibly be called.
> f(1, 1, "Y"); // err err err 2
Everybody's wrong. The right answer is f[1]!
Again, there are three actuals so only f[1] or f[2] could be candidates.
However, f[2] could not possibly be called because it is illegal in
C++ 2.1 to initialize a "double &" formal parameter from an actual "1".
See 8.4.3: "A reference to a plain T can be initialized only with a
plain T." The actual "1" has type int which is not a plain double.
(Perhaps your cfront is pre 2.1?)
Once again,
13.2: "...A call of a given function name chooses, from among all functions
by that name that are in scope and for which a set of conversions exists so
that the function could possibly be called ..."
Here the only function that could possibly be called is f[1].
That's the one which should be called.
> f(3, 1.1, 0); // err err 1 err
f[1] should be called.
Again, there are three actuals so only f[1] or f[2] could be candidates.
By the explanation above, f[2] is not a candidate.
Since a set of conversions exist which permits f[1] to be called, it
should be called, just as explained in the previous case.
> f(1.1, 3, (void*)0);// err 1 1 err
f[1] should be called.
Again, there are three actuals so only f[1] or f[2] could be candidates.
Conversions exist to call f[1] so it is a candidate.
However, f[2] cannot be called because there is no conversion
from "void *" to "const char *" in C++. Thus f[1] is the only
candidate thus, by the first paragraph of 13.2 it should be called.
> f(1, 1); // err 4 4 4
f[4] should be called. Only f[4] has the right number of formals
and conversions exist to call it. (f[2] can't be called, there's
no conversion from 1 to "double&".)
> f(pi, pi, "Y"); // 1 1 1 err
Error, ambiguous. Both f[1] and f[2] have the right number of
formals and the actuals can be converted to the formals for both
f[1] and f[2]. Finally, we have to start disambiguating by
considering costs of conversions.
Parameter 1:
f[1]: pi -> double& exact/match with trivial convs
f[2]: pi -> const double& exact/match with trivial convs
Parameter 1 best match set: { f[1], f[2] }.
Parameter 2:
f[1]: pi -> double exact/match with trivial convs
f[2]: pi -> int match with standard conversions
Parameter 2 best match set: { f[1] }.
Parameter 3:
f[1]: "Y" -> void * match with standard conversions
f[2]: "Y" -> const char * exact/match with trivial convs
("Y" is of type array of char;
this is trivially promoted to pointer to char
this is trivially promoted to pointer to const char)
Parameter 3 best match set: { f[2] }.
Intersection of best match sets: { f[1], f[2] } ^ { f[1] } ^ { f[2] } = nil
Since intersection is nil (empty), call is ambiguous.
> f(0L); // 4 3 4 4
f[4] should be called. The two candidates f[3] and f[4] have appropriate
numbers and types of arguments. Disambiguate by cost of conversions:
Parameter 1:
f[3]: 0L -> int match with standard conversions
f[4]: 0L -> long exact/match with trivial convs
Parameter 1 best match set: { f[4] }.
Thus f[4] should be called.
> f(0L, 'a'); // err 4 4 4
f[4] should be called. The two candidates with 2 arguments are
f[2] and f[4]. However, there is no conversion from 0L to "double&"
(see 8.4.3 again). Thus the only function with right numbers of
arguments and with conversions from actuals to formals is f[4].
> f(pi); // err 3 err err
Error, ambiguous.
Two possible functions, f[3] and f[4], both of which have right
numbers of arguments, and conversions exist from pi to their
formal types. Disambiguate by cost of conversions:
Parameter 1:
f[3]: pi -> int match with standard conversions
f[4]: pi -> long match with standard conversions
Parameter 1 best match set: { f[3], f[4] }.
Intersection of best match sets for this (one) argument yields
{ f[3], f[4] }. 13.2: "Unless this intersection has exactly one member,
the call is illegal.
The current overloaded function disambiguation rules are anything but
intuitive! Just wait til X3J16 tries to specify the rules for
disambiguating amongst overloaded operator functions and builtin
operator functions on objects when user defined conversion
functions are applied...
Cheers.
Jan Gray jangr@microsoft.com Microsoft Corp., Redmond, WA 206-882-8080
Author: jangr@microsoft.UUCP (Jan GRAY)
Date: 4 Jul 91 05:45:25 GMT Raw View
Silly me. I forgot that some of the compilers you are trying are
C++ 2.0 compilers. One of the differences between 2.0 and 2.1
is under C++ 2.1, it is not legal to initialize a T& with a
non-T lvalue.
So....
This posting will discuss which way ambiguities are resolved under
C++ *2.0* conversion rules. (Unfortunately, some of the functions
will disambiguate differently under 2.0 than under 2.1.)
>>#include <stream.h>
>>
>>void f (const double &, double, void *) { cout << 1; }
>>void f (double &, int, const char * = 0) { cout << 2; }
>>void f (int) { cout << 3; }
>>void f (long, int = 0) { cout << 4; }
>>
>>main ()
>>{
>> double b;
>> const double pi = 3.14;
>>
>> // Z g++ cfront BC++
>> //---------------------------------------
>
>
>> f(1.1, 1, f); // 1 err err 1
>
> Should be error.
Error under C++ 2.0 also.
>
>> f(1, 1, "Y"); // err err err 2
>
> Everybody's wrong. The right answer is f[1]!
Under C++ 2.0, f[2] should be called:
Under C++ 2.0, there are two valid candidates, since both f[1] and
f[2] might be called with the given arguments. Disambiguation:
Parameter 1:
f[1]: 1 -> const double & promotion
f[2]: 1 -> double& promotion
Parameter 1 best match set: { f[1], f[2] }
Parameter 2:
f[1]: 1 -> double promotion
f[2]: 1 -> int exact/trivial convs
Parameter 3 best match set: { f[2] }
Parameter 3:
f[1]: "Y" -> void * standard conversion
f[2]: "Y" -> const char * exact/trivial convs
Parameter 3 best match set: { f[2] }
Intersection of these sets indicates f[2] should be called.
>> f(3, 1.1, 0); // err err 1 err
>
> f[1] should be called.
Under C++ 2.0, f[1] should be called (but for different reasons):
Under C++ 2.0, there are two valid candidates, since both f[1] and
f[2] might be called with the given arguments. Disambiguation:
Parameter 1:
f[1]: 3 -> const double & promotion
f[2]: 3 -> double& promotion
Parameter 1 best match set: { f[1], f[2] }
Parameter 2:
f[1]: 1.1 -> double exact/trivial convs
f[2]: 1.1 -> int standard conversion
Parameter 3 best match set: { f[1] }
Parameter 3:
f[1]: 0 -> void * standard conversion
f[2]: 0 -> const char * standard conversion
Parameter 3 best match set: { f[1], f[2] }
Intersection of these sets yields { f[1] } so f[1] should be called.
>> f(1.1, 3, (void*)0);// err 1 1 err
>
> f[1] should be called.
Same under C++ 2.0.
>
>> f(1, 1); // err 4 4 4
Under C++ 2.0, this is an ambiguous call:
Under C++ 2.0, both f[2] and f[4] are candidates.
Parameter 1:
f[2]: 1 -> const double & promotion
f[4]: 1 -> long promotion
Parameter 1 best match set { f[2], f[4] }.
Parameter 2:
f[2]: 1 -> int exact/match with trivial convs
f[4]: 1 -> int exact/match with trivial convs
Parameter 2 best match set { f[2], f[4] }.
Interesection yields { f[2], f[4] }. Error, ambiguous.
>
>> f(pi, pi, "Y"); // 1 1 1 err
>
> Error, ambiguous.
Same under C++ 2.0.
>> f(0L); // 4 3 4 4
>
> f[4] should be called.
Same under C++ 2.0.
>> f(0L, 'a'); // err 4 4 4
>
> f[4] should be called. The two candidates with 2 arguments are
> f[2] and f[4].
Under C++ 2.0, f[4] should be called:
Under C++ 2.0, both f[2] and f[4] are candidates.
Parameter 1:
f[2]: 0L -> const double & promotion
f[4]: 0L -> long exact/match with trivial convs
Parameter 1 best match set { f[4] }.
Parameter 2:
f[2]: 'a' -> int promotion
f[4]: 'a' -> int promotion
Parameter 2 best match set { f[2], f[4] }.
Interesection yields { f[4] }. f[4] is called.
>
>
>> f(pi); // err 3 err err
>
> Error, ambiguous.
Same under C++ 2.0.
Jan.