Skip to content

Friend function template with friend after a dependent return type isn't matched by its later definition ("more than one instance ... matches") #442

Open

Description

@mborland

Issue Summary

A class template declares a friend function template with the friend
specifier written after the return type:

typename C<U>::size_type friend erase_if(C<U> &c, P p);

The front end then treats the namespace-scope definition of the same function
template as a different template, so an unqualified call reports "more than
one instance of erase_if matches the argument list". GCC and Clang accept
the code. Writing friend first (friend typename C<U>::size_type erase_if(...)),
or using a non-dependent return type, works.

Boost.Unordered declares erase_if this way for its open-addressing
containers (unordered_flat_map, unordered_flat_set, unordered_node_map,
unordered_node_set), so any call to erase_if on them fails to compile. This
fails 2 Boost.Unordered tests (foa_erase_if, foa_post_move_tests).

Reproducing Source Code

namespace N {
template <class T> class C {
  int x = 0;
  template <class U, class P>
  typename C<U>::size_type friend erase_if(C<U> &c, P p);
public:
  using size_type = unsigned long;
};
template <class T, class P>
typename C<T>::size_type erase_if(C<T> &c, P p) { return c.x; }
}
int main() { N::C<int> c; return (int)erase_if(c, 0); }

Command-line Options

cpfe --c++17 friend_erase_if.cpp

Type of Issue

[Front End] This code SHOULD compile with these options.

Additional Details

Expected: compiles (g++ 15.3.1 compiles and runs it, exit status 0).
Actual (linux-gcc-release and linux-gcc-debug; also with --c++20, and in
GNU mode through eccp):

"friend_erase_if.cpp", line 12: error: more than one instance of "erase_if"
          matches the argument list:
            function template "N::C<U>::size_type N::erase_if(N::C<U> &, P)"
                      (declared at line 5)
            function template "N::C<T>::size_type N::erase_if(N::C<T> &, P)"
                      (declared at line 10)
            argument types are: (N::C<int>, int)
  int main() { N::C<int> c; return (int)erase_if(c, 0); }
                                        ^

1 error detected in the compilation of "friend_erase_if.cpp".

Variants checked:

Declaration in the class Result
typename C<U>::size_type friend erase_if(C<U> &, P); error (above)
friend typename C<U>::size_type erase_if(C<U> &, P); compiles
int friend erase_if(C<U> &, P); (non-dependent return type) compiles

So the combination of a decl-specifier friend after the type and a
dependent return type that names the enclosing class template seems to give
the friend declaration a different signature than the matching namespace-scope
declaration.

Boost: boost/unordered/unordered_flat_map.hpp:60-62 declares

template <class K, class V, class H, class KE, class A, class Pred>
typename unordered_flat_map<K, V, H, KE, A>::size_type friend erase_if(
  unordered_flat_map<K, V, H, KE, A>& set, Pred pred);

and lines 776-783 define it at namespace scope. unordered_flat_set.hpp:57-58,
unordered_node_map.hpp:62-63 and unordered_node_set.hpp:61-62 use the same
pattern. libs/unordered/test/unordered/erase_if.cpp:70,99,112 and
post_move_tests.cpp:358 then fail with the error above.

Environment: edgcpp/dev-env Docker image (Fedora 43, GCC 15.3.1) on
x86-64, boost branch at c36e03a67c.

Reproducing (Standard) Configuration(s)

linux-gcc-release

Reproducing (Non-standard) Configuration

No response

I acknowledge that:

  • I have checked open issues and existing discussions before filing this report.
  • The reproducing source code that I have provided does not depend on headers that I have not included with my submission.
  • I have reduced the reproducing source code to the best of my ability.
  • I have selected at least one standard configuration or uploaded a non-standard configuration dump.
  • I understand code shared via this form is NOT confidential.
  • I have permission to share the reproducing source code and grant permission for the project to use the source code (or derivatives of the source code) to create tests.
  • I agree that any tests created using the reproducing source code (or deriatives of it) will be licensed under project license unless I included a LICENSE.txt file in my .zip or .tar.gz upload. If I included a LICENSE.txt file, any tests created using the reproducing source code will be governed by the terms of the license I provided instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions