Topic: Ambiguity Question


Author: jamshid@emx.cc.utexas.edu (Jamshid Afshar)
Date: 9 Sep 1993 15:31:50 -0500
Raw View
In article <KANZE.93Sep7190429@slsvhdt.us-es.sel.de>,
James Kanze <kanze@us-es.sel.de> wrote:
>In article <1993Sep3.191511.15343@cs.brown.edu> sdm@cs.brown.edu
>(Scott Meyers) writes:
>>   class Widget {};
>>   class WidgetCollection {
>>   public:
>>     Widget operator[](long) const;
>>     operator char*() const;
>>   };
>>   main()  {
>>     WidgetCollection wc;
>>     Widget w = wc[1];      // ambiguous?
>>   }

James raises some very valid points which I include at the end of this
article -- hopefully they'll start some discussion in comp.std.c++.
First, let's simplify Scott's problem to the following which I believe
is a valid analogy:

 class WidgetCollection {
 public:
    operator char*() const;
 };

 void foo( char*, int );  // foo-1: built-in operator[] for char*'s
 void foo( const WidgetCollection&, long );  // foo-2

 main() {
    WidgetCollection wc;
    foo( wc, 1 );
 }

Overloading resolution is performed in three steps.  First, determine
the set of "best-matching" functions for *each* parameter in the
function call *independently* of the other parameters.  Then take the
intersection of these (possibly one-element) sets.  If the
intersection of these sets is exactly one function, that function is
the winner.  The final, more subtle, requirement is that the winning
function must be a strictly better match than any other function for
at least one argument.  See ARM 13.2 or CPL2 r13.2 for a full
explanation.

Actual parameter 1 is a `WidgetCollection'.  The type of function
`foo-1's first parameter is a `char*'.  The type of `foo-2's first
parameter is a `const WidgetCollection&'.  The argument matching scale
from 13.2 is used to "rank" conversions:

    exact         with        with standard   with user-defined     with
    match      promotions      conversions       conversions      ellipses
      1            2               3                 4                5
eg: T=>T&      char=>int       int=>long        char*=>String
    T*=>       short=>int      int=>double
    const T*   enum=>int       0=>T*
               float=>double   Derived*=>Base*

Matching `foo-1's first parameter requires a user-defined conversion.
Matching `foo-2's first parameter is an "exact match".  Therefore, the
best-matching function for the first parameter is `foo-2'.

The second actual parameter is an `int' (remember, literals are not
`const').  It is an exact match for `foo-1's second parameter, but
would require a "standard conversion" to match `foo-2's second
parameter of type `long'.  So, `foo-1' is the best-matching function
for the second parameter.

The intersection of the sets { foo-1 } and { foo-2 } is the empty
set.  Therefore, the call `foo(wc,1)' is ambigous.

>My interpretation of the ARM is that the first conversion is correct,
>but this interpretation is based on some rather vague premises.
>First, in section 13.2, the ARM describes argument matching for
>functions.  Nowhere does it say that this argument matching is valid
>for built-in operators.  I will only suppose that for the purposes of
>analysing the above expression, it is the intention of the ARM to act
>as if there was a function definition for the built-in operator[].
>This is a very big exterpolation on my part; there are no words to
>this effect in the ARM.  I do so only because I can find no other
>basis on which to evaluate the above.
>The question then comes up concerning the signature of the built-in
>operator[].  If the signature were the intuitive 'T* operator[]( T* ,
>int )', then there would be no doubt that the above would be
>ambiguous.  But in section 5.2.1, it states (concerning subscripting)
>that "one of the expresssions must have the type "pointer to T" and
>the other must be of integral type."  This would seem to imply that
>there are in fact two operator[]'s to be considered here), one with an
>int as second parameter, and one with a long.  (More generally, there
>would be one for every integral type as second parameter.)  And since
>the one with the long is an exact match for all of the parameters, it
>is the best match.

I agree that more needs to be said about overloading resolution with
respect to built-in operators.  I also agree with your analysis and
interpretation, up to the last sentence.  Other functions that should
be considered in my example are:

 void foo( char*, long );  // built-in operator[](char*,long)
 void foo( char*, unsigned );  // built-in operator[](char*,unsigned)
 void foo( char*, unsigned long );  // ...
 //... same for `const char*'

But I still believe my analysis is correct because these functions are
never better matches for `foo(wc,1)'.  Based on your last sentence
above, you seem to think that the call in the original example used a
`long' index instead of an `int'. [late-breaking news: James just
posted a correction to his article but I'm too tired to rewrite mine]

I'm not sure whether, for argument matching purposes, versions of
`operator[]' taking `short' (and enums and bitfields) are considered
to exist.  Maybe only the `int' version is considered to exist and
shorts, enums, etc. must match by promotion.  I'm also not sure
whether it could make a difference in overloading resolution.  I can't
wait until the Working Document I ordered arrives; has this been
discussed in committee meetings?

Jamshid Afshar
jamshid@emx.cc.utexas.edu




Author: kanze@us-es.sel.de (James Kanze)
Date: 10 Sep 93 12:34:42
Raw View
In article <26o3rmINNd9l@emx.cc.utexas.edu> jamshid@emx.cc.utexas.edu
(Jamshid Afshar) writes:

|> I'm not sure whether, for argument matching purposes, versions of
|> `operator[]' taking `short' (and enums and bitfields) are considered
|> to exist.  Maybe only the `int' version is considered to exist and
|> shorts, enums, etc. must match by promotion.  I'm also not sure
|> whether it could make a difference in overloading resolution.  I can't
|> wait until the Working Document I ordered arrives; has this been
|> discussed in committee meetings?

Don't expect too much from the current working document.  The
committee is aware of weaknesses in the overloading rules, and is
working on it.  But we do not yet have a concrete proposal, let alone
changes in the working papers to reflect it.  WRT overloading, the
current version of the working papers pretty much reflects the ARM,
but I am relatively sure that the first public draft will be
significantly different.

I might point out that unless you are interested in the
standardization effort per se, or are involved in writing a compiler,
I would suggest that you ignore it and stick to the ARM for the
present.  And in these two cases, I would suggest actually joining the
committee, despite its price, and actively participating in the
working groups.
--
James Kanze                             email: kanze@us-es.sel.de
GABI Software, Sarl., 8 rue du Faisan, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                   -- Beratung in industrieller Datenverarbeitung




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Fri, 10 Sep 1993 18:28:33 GMT
Raw View
In article <1993Sep3.191511.15343@cs.brown.edu> sdm@cs.brown.edu (Scott Meyers) writes:
>Consider this program:
>
>  class Widget {};
>
>  class WidgetCollection {
>  public:
>    Widget operator[](long) const;
>    operator char*() const;
>  };
>
>  main()
>  {
>    WidgetCollection wc;
>    Widget w = wc[1];      // ambiguous?
>  }
>
>The three compilers I have access to complain not at all, but I've been
>told there is at least one compiler out there that will flag the indicated
>line as ambiguous, because it can be interpreted in either of the following
>ways:
>
>    wc.operator[](1);         // convert int to long
>    wc.operator char*()[1];   // convert WidgetCollection to char*

As I have already told Scott in private E-mail, the overload resolution rules
which apply in cases where there might be a choice between calling a user-
defined operator OR a built-in (language defined) operator and entirely
less than clear.  Neither the ARM not the current X3J16 working paper
adequately explains what is SUPPOSED to any in ANY such cases.

--

-- Ronald F. Guilmette ------------------------------------------------------
------ domain address: rfg@netcom.com ---------------------------------------
------ uucp address: ...!uunet!netcom.com!rfg -------------------------------