On Tue, 10 Jan 2023 at 22:00, Lénárd Szolnoki via Std-Discussion <std-discussion@lists.isocpp.org> wrote:On Tue, 10 Jan 2023 17:47:38 +0000
Edward Catmur via Std-Discussion <std-discussion@lists.isocpp.org>
wrote:
> On Tue, 10 Jan 2023 at 17:05, Brian Bi <bbi5291@gmail.com> wrote:
>
> >
> >
> > On Tue, Jan 10, 2023 at 11:24 AM Edward Catmur via Std-Discussion <
> > std-discussion@lists.isocpp.org> wrote:
> >
> >>
> >>
> >> On Tue, 10 Jan 2023 at 15:57, language.lawyer--- via
> >> Std-Discussion < std-discussion@lists.isocpp.org> wrote:
> >>
> >>> > I would like an example of a function call to an overloaded
> >>> > function, where the most viable function is determined by the
> >>> > rules listed from [over.ics.rank]/4.4.5 to
> >>> > [over.ics.rank]/4.4.8:
> >>> >
> >>> > https://timsong-cpp.github.io/cppwp/n4868/over.ics.rank#4.4.5
> >>> >
> >>> > These are four different tie-breakers for ranking standard
> >>> > conversions from different source types, assuming the following
> >>> > class hierarchy:
> >>> >
> >>> > struct A {}; struct B : C {}; struct C : B {};
> >>> >
> >>> > (4.4.5) conversion of B* to A* is better than conversion of C*
> >>> > to A*, (4.4.6) binding of an expression of type B to a
> >>> > reference to type A is better than binding an expression of
> >>> > type C to a reference to type A, (4.4.7) conversion of B::* to
> >>> > C::* is better than conversion of A::* to C::*, and
> >>> > (4.4.8) conversion of B to A is better than conversion of C to
> >>> > A.
> >>> >
> >>> > The note says that these are only used for tie-breakers in the
> >>> > second conversion sequence of user-defined conversions:
> >>> >
> >>> > https://timsong-cpp.github.io/cppwp/n4868/over.ics.rank#note-1
> >>>
> >>> I think it is just a wrong Note.
> >>> CD2 (November 96
> >>> https://www.open-std.org/jtc1/sc22/wg21/docs/wp/html/cd2/over.html#over.ics.rank)
> >>> Note was saying:
> >>> > [Note: it is necessary to compare conversions with different
> >>> > target types in the context of an initialization by
> >>> > user-defined conver- sion; see _over.match.best_. ]
> >>>
> >>> Which also seems not 100% correct (incomplete?), because it
> >>> mentions (only?) target, and not source types.
> >>>
> >>> The next WP (Oct'97
> >>> https://www.open-std.org/jtc1/sc22/wg21/docs/wp/html/oct97/over.html#over.ics.rank)
> >>> Note gained its current (defective) wording.
> >>>
> >>> (The bullets themselves have been added by
> >>> https://www.open-std.org/JTC1/sc22/wg21/docs/papers/1995/N0661.asc)
> >>>
> >>> So, ecatmur's example seems to be relevant here, except that the
> >>> Note is not about the second standard conversion sequence after
> >>> different user conversion functions, but about
> >>> https://timsong-cpp.github.io/cppwp/n4868/over.match.best#general-2.2
> >>>
> >>
> >> Ah. So, currently, the Note says:
> >>
> >> > Compared conversion sequences will have different source types
> >> > only in
> >> the context of comparing the second standard conversion sequence
> >> of an initialization by user-defined conversion (see
> >> [over.match.best]); in all other contexts, the source types will
> >> be the same and the target types will be different.
> >>
> >> Whereas [over.match.best.general]/2.2 says:
> >>
> >> > ... the context is an initialization by user-defined conversion
> >> > (see
> >> [dcl.init], [over.match.conv], and [over.match.ref]) and the
> >> standard conversion sequence from the return type of F1 to the
> >> destination type (i.e., the type of the entity being initialized)
> >> is a better conversion sequence than the standard conversion
> >> sequence from the return type of F2 to the destination type ...
> >>
> >> Would it make sense to amend the latter to mention the second SCS?
> >> i.e.,
> >> > ... the context is an initialization by user-defined conversion
> >> > (see
> >> [dcl.init], [over.match.conv], and [over.match.ref]) and the
> >> <ins>second</ins> standard conversion sequence <ins>of ICSj(F1)
> >> (i.e., that</ins> from the return type of F1 to the
> >> <del>destination type (i.e.,</del> the type of the entity being
> >> initialized) is a better conversion sequence than the
> >> <ins>second</ins> standard conversion sequence <del>from the
> >> return type of F2 to the destination type</del><ins>of
> >> ICSj(F2)</ins> ...
> >
> > I think if we need to clarify it, we should just add a note.
> > Changing normative wording always risks unforeseen consequences.
> >
>
> Ah, Well, should we change the offending Note (to [over.ics.rank])
> then?
>
> > Compared conversion sequences will have different source types
> > only in
> the context of <del>comparing the second standard conversion sequence
> of</del> an initialization by user-defined conversion<ins>,
> specifically when comparing the standard conversion sequence from the
> return type of a viable function to the destination type</ins> (see
> [over.match.best]); in all other contexts, the source types will be
> the same and the target types will be different.
I might have found a case where no user-defined conversions are
involved:
struct A {
void foo();
};
struct B : A {
using A::foo;
void foo(int);
};
struct C : B {};
void f(void (C::*)()); // 1
void f(void (C::*)(int)); // 2
int main() {
// if conversion from void (B::*)(int) to void (C::*)(int)
// is better than void (A::*)() to void (C::*)()
// then the second overload must be called
f(&B::foo);
}
I think https://eel.is/c++draft/over.ics.rank#4.5.7 applies, although
only gcc implements this.
https://godbolt.org/z/7corz5539
Anyway, if this applies, then the note wrongly states that these only
apply in the context of user-defined conversions.Oh, brilliant. OTOH, where the pointer is used directly, there's full agreement:struct A { void foo(); };
struct B : A { using A::foo; void foo(); };
struct C : B {};
void (C::*p())() { return &C::foo; } // B::fooBut not if we write it like this; only MSVC accepts:struct A { void foo(); };
struct B : A { void foo(); };
struct C : B { using A::foo; using B::foo; };
void (C::*p())() { return &C::foo; } // B::foo (MSVC only)Do you have any thoughts on this?And there's another issue with this, though; https://eel.is/c++draft/over.over#2 says:> ... a non-template function with type F is selected for the function type FT of the target type if F (after possibly applying the function pointer conversion ([conv.fctptr])) is identical to FT.[Note: That is, the class of which the function is a member is ignored when matching a pointer-to-member-function type. — end note]This Note is problematic; what is meant by "matching"? This is too early for overload resolution, but looking at the context in which it was added https://cplusplus.github.io/CWG/issues/1153.html it's clear (to me) that instead this should read "That is, the class of which the function is a member is ignored when <del>matching</del><ins>selecting functions for</ins> a pointer-to-member-function type.".Also, we may select member functions that cannot be converted to the target pointer to member function type:struct A { void foo(); };
struct B : A { using A::foo; void foo(); };
struct C : B {};
struct D : C { using B::foo; void foo(); };
void (C::*p())() { return &D::foo; }Unfortunately, everyone rejects this, but IMO this should be valid; it should remove D::foo from the overload set before ranking.
So I would have [over.over]/2 refer to [conv.mem] and amend the Note:> ... a non-template function is selected if the type of a pointer to the function can be converted to the target type via a standard conversion sequence consisting only of pointer-to-member conversions ([conv.mem]) and function pointer conversions ([conv.fctptr]). [Note: The function type of the target type must thus be identical to the function type of the function. --end note]In any case, we now may have a set of (pointer to member) functions that may be ranked according to [over.ics.rank]/4.5.7. But [over.over]/5 doesn't tell us to do this, or indeed when to do this!If we give B::f unsatisfied constraints (as a function template specialization), everyone (gcc, clang and MSVC, that is - I don't have an EDG with concepts) is happy to select A::f:struct A {
template<int...> int f();
};
struct B : A {
using A::f;
template<int...> int f() requires(false);
};
int (B::*p())() { return &B::f; }But if we make B::f a non-template function with unsatisfied constraints, MSVC rejects:template<int...> struct A {
int f();
};
template<int...> struct B : A<> {
using A<>::f;
int f() requires(false);
};
int (B<>::*p())() { return &B<>::f; }If we make B::f a function template specialization and A::f a non-template function, everyone except EDG prefers A::f:struct A {
int f();
};
struct B : A {
using A::f;
template<int...> int f();
};
int (B::*p())() { return &B::f; } // A::f (gcc, clang, MSVC), B::f<> (EDG)If we make A::f and B::f function template specializations with A::f more constrained than B::f, gcc, clang and MSVC prefer A::f:struct A {
template<int...> int f() requires(true);
};
struct B : A {
using A::f;
template<int...> int f();
};
int (B::*p())() { return &B::f; } // A::f<> (gcc, clang, MSVC)But if we make A::f and B::f non-template functions with A::f more constrained than B::f, gcc and clang error while MSVC prefers B<>::f:template<int...> struct A {
int f() requires(true);
};
template<int...> struct B : A<> {
using A<>::f;
int f();
};
int (B<>::*p())() { return &B<>::f; } // &A<>::f (more constrained) or &B<>::f (better pointer-to-member)?For consistency with [over.match.best.general]/2, which puts [over.ics.rank] (at 2.1) ahead of [temp.constr.order] (at 2.6), I would select B::f in all cases and amend [over.over]/5:> All functions with associated constraints that are not satisfied ([temp.constr.decl]) are eliminated from the set of selected functions. <ins>Any given member function MF1 is eliminated if the set contains a second member function MF2 such that the standard conversion sequence from the type of a pointer to MF2 to the target type is better than the standard conversion sequence from the type of a pointer to MF1 to the target type. [Note: when ranking such standard conversion sequences, only pointer-to-member conversions ([conv.mem]) are considered. --end note]</ins> If more than one function in the set remains ...Wow. So now we have a further fix to the Note to [over.ics.rank]):> Compared conversion sequences will have different source types only in the context of <del>comparing the second standard conversion sequence of</del> an initialization by user-defined conversion<ins>, specifically when comparing the standard conversion sequence from the return type of a viable function to the destination type</ins> (see [over.match.best]) <ins>or of taking the address of an overload set consisting of member functions ([over.over])</ins>; in all other contexts, the source types will be the same and the target types will be different.And [over.ics.rank]/3.3 still has the dubious wording about aggregate initialization.