Topic: Argument matching and illegal matches


Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Thu, 9 Sep 1993 09:41:17 GMT
Raw View
In article <25v9ik$fn8@lucy.ee.und.ac.za> barrett@lucy.ee.und.ac.za (Alan Barrett) writes:
>In article <CCLEEM.K3z@sugar.neosoft.com>, daniels@NeoSoft.com (Brad Daniels) writes:
...
>>
>> Meaning that the intersection is the function which is not legal, thus
>> making the call illegal.
>
>That's right, according to my reading of the ARM.
>
>> Is this truly the intent of the ARM, or is it simply a weird side-effect?
>
>I believe that it is truly the intent of the ARM.  The intent is
>perhaps made more clear in yet another paragraph from section 13.2 of
>the reference proper:
>
>*   A temporary variable is needed [sometimes].  This does not affect
>*   argument matching.  It may, however, affect the legality of the
>*   resulting match since a temporary may not be used to initialise
>*   a non-"const" reference (\sec 8.4.3).
>
>It seems clear to me that the ARM intended matching to be performed
>independently of the legality of the resulting match.

That's not clear at all!  As a matter of fact, the ARM quite plainly
contradicts itself on this point.  (See below.)

>> It seems to me that illegal matches should be eliminated before
>> determining the best match.  In the absence of verbiage saying it
>> doesn't do so, one would tend to assume that the language would
>> require that illegal matches not be considered when determining the
>> best match.

You would think so wouldn't you!

>I can find no *explicit* verbiage in the ARM saying that illegal
>matches are not eliminated, but the paragraph that I quoted above
>strongly suggests that the result from the matching process may be a
>function that may not legally be called in the current context.
>Further, there is an explicit definition of the matching process, and
>this definition does not include the elimination of illegal matches.
>Thus, it seems clear that illegal matches are not eliminated during
>matching (according to the ARM).

Right, but they *may* be eliminated BEFORE mathing starts!

Consider the following quotation from the very start of 13.2 of the ARM:

 "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
 exist so that the function could possibly be called, the function
 that best matches the arguments."

This passage seems to be in direct contradiction with the following later
passage from 13.2 (which was also quoted above):

>*   A temporary variable is needed [sometimes].  This does not affect
>*   argument matching.  It may, however, affect the legality of the
>*   resulting match since a temporary may not be used to initialise
>*   a non-"const" reference (\sec 8.4.3).

--

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




Author: barrett@lucy.ee.und.ac.za (Alan Barrett)
Date: 31 Aug 1993 12:36:04 +0200
Raw View
[I changed the Subject from "Re: For Your Amusement: My Favorite C++ Bug"]

In article <CCLEEM.K3z@sugar.neosoft.com>,
daniels@NeoSoft.com (Brad Daniels) writes:
> In article <1993Aug30.192359.22584@parc.xerox.com>
> ellis@parc.xerox.com (John Ellis) writes:
> >Here are selected quotations from the May, 1991 reprint of the ARM:

I think that it is important to distinguish the commentary and annotations
from the reference proper, and I will do so below.

> >-   Note that functions with arguments of type T, const T, volatile T,
> >    T&, const T&, and volatile T& accept exactly the same set of
> >    values [for the purposes of argument matching]. [page 318]

This is part of section 13.2 in the reference section of the ARM.

> >-   A temporary variable is needed for a formal argument of type T&
> >    if the actual argument is not an lvalue, has a type different from
> >    T, or is a volatile and T isn't.  This does not affect argument
> >    matching.  It may, however, affect the legality of the resulting match
> >    since a temporary may not be used to initialize a non-const
> >    reference (r8.4.3). [page 318]

This is part of section 13.2 in the reference section of the ARM.

> >-   In other words, "constness" acts as a tie-breaker where needed
> >    but does not affect argument matching otherwise. [page 320]

This is part of an annotation to section 13.2 in the ARM, not part of
the reference manual proper.  Nevertheless, it follows directly from
the following material in section 13.2 of the reference proper:

*   Except as mentioned below, the following _trivial conversions_
*   involving a type "T" do not affect which of two conversion
*   sequences is better:
*
*       from:                to:
*        T                    T&
*        T&                   T
*        T[]                  T*
*        T(args)              (*T)(args)
*        T                    const T
*        T                    volatile T
*        T*                   const T*
*        T*                   volatile T*
*
*   [...]  Where necessary, "const" and "volatile" are used as
*   tie-breakers as described in rule [1] below.
*
*   [...]
*
*   Sequences of conversions are considered according to these rules:
*
*     [1]  Exact match:  Sequences of zero or more trivial conversions
*          are better than all other sequences.

Hence, const-ness and volatile-ness are effectively ignored, but:

*                                                  Of these, those
*          that do not convert "T*" to "const T*", "T*" to
*          "volatile T*", "T&" to "const T&", or "T&" to
*          "volatile T&" are better than those that do.

Hence, const-ness and volatile-ness are used as tie-breakers.

> Reading these, I see your contention more clearly.  To summarize:
>
> ostream &operator<<(ostream&,const char*)
> const ostream &operator<<(const ostream&,const aClass&)
>
> While the set best matching on the second argument is:
>
> ostream &operator<<(ostream&,const char*)
>
> Meaning that the intersection is the function which is not legal, thus
> making the call illegal.

That's right, according to my reading of the ARM.

> Is this truly the intent of the ARM, or is it simply a weird side-effect?

I believe that it is truly the intent of the ARM.  The intent is
perhaps made more clear in yet another paragraph from section 13.2 of
the reference proper:

*   A temporary variable is needed [sometimes].  This does not affect
*   argument matching.  It may, however, affect the legality of the
*   resulting match since a temporary may not be used to initialise
*   a non-"const" reference (\sec 8.4.3).

It seems clear to me that the ARM intended matching to be performed
independently of the legality of the resulting match.

> It seems to me that illegal matches should be eliminated before
> determining the best match.  In the absence of verbiage saying it
> doesn't do so, one would tend to assume that the language would
> require that illegal matches not be considered when determining the
> best match.

I can find no *explicit* verbiage in the ARM saying that illegal
matches are not eliminated, but the paragraph that I quoted above
strongly suggests that the result from the matching process may be a
function that may not legally be called in the current context.
Further, there is an explicit definition of the matching process, and
this definition does not include the elimination of illegal matches.
Thus, it seems clear that illegal matches are not eliminated during
matching (according to the ARM).

>              An illegal match does not satisfy
> any reasonable definition of "best match".

But it does satisfy the definition given in the ARM, and I think that
there is some sense to it.  I would prefer a compile error ("best
matching function may not legally be called here") to the infinite
recursion that results, in the absence of this rule, from the example
that started this discussion.

  [fast forward]
> Indeed, the annotations you quote all seem to be discussing a particular,
> and in my opinion flakey, implementation of C++, rather than language
> issues.  It's certainly relevant if one is using said implementation,
> but it's hardly something that should be codified into a standard.

John Ellis's quotations were not all from annotations.  Some were
from the reference proper, as I indicated at the beginning of this
article.

I would be interested in learning which, if any, of my or John Ellis's
quotations from the reference section of the ARM are missing from, or
modified in, the current working documents of the standardisation
committee, and how, if at all, that affects the situation under
discussion.

--apb
Alan Barrett, Dept. of Electronic Eng., Univ. of Natal, Durban, South Africa
RFC822: barrett@ee.und.ac.za




Author: daniels@NeoSoft.com (Brad Daniels)
Date: Tue, 31 Aug 1993 19:33:37 GMT
Raw View
In article <25v9ik$fn8@lucy.ee.und.ac.za> barrett@lucy.ee.und.ac.za (Alan Barrett) writes:
>[I changed the Subject from "Re: For Your Amusement: My Favorite C++ Bug"]
>
>In article <CCLEEM.K3z@sugar.neosoft.com>,
>daniels@NeoSoft.com (Brad Daniels) writes:
>> In article <1993Aug30.192359.22584@parc.xerox.com>
>> ellis@parc.xerox.com (John Ellis) writes:
>> >Here are selected quotations from the May, 1991 reprint of the ARM:
>
>I think that it is important to distinguish the commentary and annotations
>from the reference proper, and I will do so below.
>
>> >-   Note that functions with arguments of type T, const T, volatile T,
>> >    T&, const T&, and volatile T& accept exactly the same set of
>> >    values [for the purposes of argument matching]. [page 318]
>
>This is part of section 13.2 in the reference section of the ARM.
>
>> >-   A temporary variable is needed for a formal argument of type T&
>> >    if the actual argument is not an lvalue, has a type different from
>> >    T, or is a volatile and T isn't.  This does not affect argument
>> >    matching.  It may, however, affect the legality of the resulting match
>> >    since a temporary may not be used to initialize a non-const
>> >    reference (r8.4.3). [page 318]
>
>This is part of section 13.2 in the reference section of the ARM.

Both of these passages are in the working document.

>
>> >-   In other words, "constness" acts as a tie-breaker where needed
>> >    but does not affect argument matching otherwise. [page 320]
>
>This is part of an annotation to section 13.2 in the ARM, not part of
>the reference manual proper.  Nevertheless, it follows directly from
>the following material in section 13.2 of the reference proper:
>
>*   Except as mentioned below, the following _trivial conversions_
>*   involving a type "T" do not affect which of two conversion
>*   sequences is better:
>*
>*       from:                to:
>*        T                    T&
>*        T&                   T
>*        T[]                  T*
>*        T(args)              (*T)(args)
>*        T                    const T
>*        T                    volatile T
>*        T*                   const T*
>*        T*                   volatile T*
>*
>*   [...]  Where necessary, "const" and "volatile" are used as
>*   tie-breakers as described in rule [1] below.
>*
>*   [...]
>*
>*   Sequences of conversions are considered according to these rules:
>*
>*     [1]  Exact match:  Sequences of zero or more trivial conversions
>*          are better than all other sequences.
>
>Hence, const-ness and volatile-ness are effectively ignored, but:

I fail to see how the above text implies what you say it does.
Note that the case under discussion involves a const object being
matched against a non-const function.  There is no trivial conversion
to achieve such a change, and hence there should be no match (were it
not for the additional text in the annotation.)  The "tie breaker"
comment seems to imply that a non-const object would match more
closely with a non-const parameter than with a const one, and does
not directly imply anything about a const object matching a non-const
parameter.  The only place in the working document which provides a
relatively straightforward basis for arriving at the analysis described
in the ARM is the subsequent text, quoted earlier, about a resulting
match being incorrect, though you will notice that the paragraph refers
only to conversions to reference types where the actual argument is
not an lvalue, has a different type, or is volatile when the formal
argument isn't.  It makes no mention of const in such a context.

>
>*                                                  Of these, those
>*          that do not convert "T*" to "const T*", "T*" to
>*          "volatile T*", "T&" to "const T&", or "T&" to
>*          "volatile T&" are better than those that do.
>
>Hence, const-ness and volatile-ness are used as tie-breakers.

Correct, but again note the direction of the conversions.

...

>> Is this truly the intent of the ARM, or is it simply a weird side-effect?
>
>I believe that it is truly the intent of the ARM.  The intent is
>perhaps made more clear in yet another paragraph from section 13.2 of
>the reference proper:
>
>*   A temporary variable is needed [sometimes].  This does not affect
>*   argument matching.  It may, however, affect the legality of the
>*   resulting match since a temporary may not be used to initialise
>*   a non-"const" reference (\sec 8.4.3).
>
>It seems clear to me that the ARM intended matching to be performed
>independently of the legality of the resulting match.

At this point, I concede that it does seem to be the intent of the
ARM to forward this approach, but there is no unambiguous support
of such an approach in the reference section or in the working document,
and indeed, the reference text encourages an interpretation closer to
the one I had taken, most notably by never referring to considering
an illegal conversion from const to non-const using a temporary as a
valid criterion for matching, but making such comments about other
illegal conversions.

- Brad
--
Brad Daniels   |  "Let others praise ancient times.
daniels@neosoft.com  |   I am glad I was born in these."
I don't work for NeoSoft, and | - Ovid (43 B.C. - 17 A.D)
don't speak for my employer. |




Author: barrett@lucy.ee.und.ac.za (Alan Barrett)
Date: 1 Sep 1993 08:49:10 +0200
Raw View
In article <CCn2C2.LJz@sugar.neosoft.com>,
daniels@NeoSoft.com (Brad Daniels) writes:
> In article <25v9ik$fn8@lucy.ee.und.ac.za>
> barrett@lucy.ee.und.ac.za (Alan Barrett) writes:
> >> >-   In other words, "constness" acts as a tie-breaker where needed
> >> >    but does not affect argument matching otherwise. [page 320]
> >This is part of an annotation to section 13.2 in the ARM, not part of
> >the reference manual proper.  Nevertheless, it follows directly from
> >the following material in section 13.2 of the reference proper:
> >
> >*   Except as mentioned below, the following _trivial conversions_
> >*   involving a type "T" do not affect which of two conversion
> >*   sequences is better:
> >*
> >*       from:                to:
> >*        T                    T&
> >*        T&                   T
> >*        T[]                  T*
> >*        T(args)              (*T)(args)
> >*        T                    const T
> >*        T                    volatile T
> >*        T*                   const T*
> >*        T*                   volatile T*
> >*
> >*   [...]  Where necessary, "const" and "volatile" are used as
> >*   tie-breakers as described in rule [1] below.
> >*
> >*   [...]
> >*
> >*   Sequences of conversions are considered according to these rules:
> >*
> >*     [1]  Exact match:  Sequences of zero or more trivial conversions
> >*          are better than all other sequences.
> >
> >Hence, const-ness and volatile-ness are effectively ignored, but:
>
> I fail to see how the above text implies what you say it does.
> Note that the case under discussion involves a const object being
> matched against a non-const function.  There is no trivial conversion
> to achieve such a change, and hence there should be no match (were it
> not for the additional text in the annotation.)

Ah, yes, you are right.  I had neglected to take note of the direction
of the conversion.  The ARM anotations are not part of the ARM language
definition, and the absence of a conversion (whether trivial or not)
from const to non-const means that that match cannot be made.

--apb
Alan Barrett, Dept. of Electronic Eng., Univ. of Natal, Durban, South Africa
RFC822: barrett@ee.und.ac.za




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 1 Sep 1993 15:37:13 GMT
Raw View
In article <25v9ik$fn8@lucy.ee.und.ac.za> barrett@lucy.ee.und.ac.za (Alan Barrett) writes:
>daniels@NeoSoft.com (Brad Daniels) writes:
>> ellis@parc.xerox.com (John Ellis) writes:
>> >Here are selected quotations from the May, 1991 reprint of the ARM:
>
>*   Except as mentioned below, the following _trivial conversions_
>*   involving a type "T" do not affect which of two conversion
>*   sequences is better:

 The ARM is badly confused here. Most of these 'trivial'
conversions are not conversions at all. IMHO :-)

>*
>*       from:                to:
>*        T                    T&

 Not a conversion. Its a binding rule, and the rule is that
this binding is ILLEGAL.

>*        T&                   T

 Not a conversion. This is sometimes called 'decay' of
a lvalue into a non-lvalue. IMHO there is no place where this
'rule' can possibly be used, so it should be ignored.

>*        T[]                  T*
>*        T(args)              (*T)(args)

 I think these might as well be considered actual
conversions, though I'm not sure.

>*        T                    const T
>*        T                    volatile T

 These rules are now irrelevant. The declarations:

 f(int);
 f(const int);
 f(volatile int);
 f(const volatile int);

all declare the same function (Determined at Munich). In effect,
there is no 'non-lvalue' type with a cv-qualifier. Although the
Munich resolution was not explicit about it, the same applies
to initialisations like:

 int x = thing;
 const int x = thing;
 volatile int x = thing;
 const volatile x = thing;

This is because initialisations like these and parameter passing
are the same phenomenon: there is no distinction between them.
The 'const' and 'volatile' on the LHS of the variable declarations
above is irrelevant to the initialisation of the object 'x' by
'thing'. It is meaningful *only* on subsequent uses of 'x',
when 'x' is an lvalue and effectively has the type 'int&',
'int const&',  'int volatile&' or 'int const volatile&'.

There is no conversion involved here, other than possibly
from the type of 'thing' to the type 'int'. What is involved
is a *binding* of the symbol 'x' to an object that has
already been initialised (by thing).

>*        T*                   const T*
>*        T*                   volatile T*

 These are real conversions, although they can usually
be optimised away.  However, there are some new conversions
to consider:

 T **    to   T const * const *

for example. What about this:

 f(T const * const&);
 T **x;
 f(*x);

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA