Skip to content

[temp.over.link] Referring to member functions distinguished by non-functionally-equivalent constraints #256

Open
@ecatmur

Description

@ecatmur

[temp.over.link]/5 Example 4 demonstrates that the following is well-formed:

template<int I> concept C = true;
template<typename T> struct A {
  void f() requires C<42>;      // #1
  void f() requires true;       // OK, different functions
};

This is OK since the two constraints are unevaluated operands and so functional equivalence is considered operationally.

However, changing return types allows us to refer to the two member functions separately:

template<int I> concept C = true;
template<typename T> struct A {
    int f() requires C<42> { return 1; }  // #1
    unsigned f() requires true { return 2u; }  // #2
};
int main() {
    int (A<int>::*p)() = &A<int>::f;
    unsigned (A<int>::*q)() = &A<int>::f;
    return (A<int>().*p)() + (A<int>().*q)();
}

There is implementation divergence; clang errors ("definition with same mangled name '_ZN1AIiE1fEv' as another definition"), gcc errors ("Two symbols with same comdat_group are not linked by the same_comdat_group list") and then ices ("symtab_node::verify failed") while MSVC accepts (helped by the ABI mangling return types).

If the return types are changed to auto (auto f() requires C<42> { return 1; } etc.), clang accepts erroneously, calling #.2 both times, gcc continues to error and ice, and MSVC rejects C2440 ("initializing': cannot convert from 'overloaded-function' to 'int (__cdecl A::* )(void)'", etc.)

I'm not sure what the right fix is. It seems like it'd be best for #.1 and #.2 to be determined to be incompatible overloads at the time of class template instantiation, but maybe that'd come with other issues.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions