Following code does not compile on any gcc version regardless of chosen C++ standard version, but compiles correctly on clang, msvc and icc. struct A { virtual void foo() = 0; virtual void bar() = 0; }; template <typename T, T val> struct const_val{}; template <int N, typename T> struct indexed_elem{}; using mem_fun_A_foo = const_val<decltype(&A::foo), &A::foo>; using mem_fun_A_bar = const_val<decltype(&A::bar), &A::bar>; struct A_indexed_member_funcs : indexed_elem<0, mem_fun_A_foo>, indexed_elem<1, mem_fun_A_bar> {}; template <typename T, int N> constexpr int index_of(indexed_elem<N, T>) { return N; } void test() { static_assert(index_of<mem_fun_A_foo>(A_indexed_member_funcs{}) == 0, ""); }
Ping
Confirmed, presumable we (gcc) are wrong here.
Still broken in gcc 10.2 and trunk, also in C++20 mode. The problem seems to show up only when address of member function (virtual or not) is passed as template parameter - only then gcc fails during overload resolution. For free functions this works fine. struct A { void foo(); void bar(); }; template <int N, auto val> struct indexed_elem{}; struct A_indexed_member_funcs : indexed_elem<0, &A::foo>, indexed_elem<1, &A::bar> {}; template <auto val, int N> constexpr int index_of(indexed_elem<N, val>) { return N; } void foo(); void bar(); struct indexed_free_funcs : indexed_elem<0, &foo>, indexed_elem<1, &bar> {}; void test() { // this fails due to gcc claim of ambiguous base classes static_assert(index_of<&A::foo>(A_indexed_member_funcs{}) == 0, ""); // this is ok static_assert(index_of<&foo>(indexed_free_funcs{}) == 0, ""); }
I have done some more experiments, and it seems the problem applies to all pointer-to-member (not just pointer to member function, but also for pointer to data member). Also it doesn't matter if these pointer-to-members are all from same class or not, error is the same.
I think I was able to narrow it down to the true root cause. Following fails in all gcc versions that supports C++11 and newer: struct foo { void baz(); void bar(); }; static_assert(&foo::baz != &foo::bar, ""); You may ask why I think so, because when we compile following code with -std=c++17 in gcc6.x, gcc7.x or gcc8.x: struct foo { void baz(); void bar(); }; constexpr auto foo_baz() { return &foo::baz; } constexpr auto foo_bar() { return &foo::bar; } template <void (foo::*)()> struct member_fun_ptr; using member_foo_baz = member_fun_ptr<foo_baz()>; using member_foo_bar = member_fun_ptr<foo_bar()>; we get following errors: <source>:20:48: error: 'void (foo::*)(){foo::baz, 0}' is not a valid template argument for type 'void (foo::*)()' using member_foo_baz = member_fun_ptr<foo_baz()>; ^ <source>:20:48: note: it must be a pointer-to-member of the form '&X::Y' <source>:21:48: error: 'void (foo::*)(){foo::bar, 0}' is not a valid template argument for type 'void (foo::*)()' using member_foo_bar = member_fun_ptr<foo_bar()>; which indicates that both &foo::baz and &foo::bar both have value '0' in compile time, which is certainly not correct (and that is most likely why we get "ambiguous base class" error in the example from comment#1).
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>: https://gcc.gnu.org/g:c822ad86984e752734b9c371f9cfef9330334ec4 commit r14-6519-gc822ad86984e752734b9c371f9cfef9330334ec4 Author: Patrick Palka <ppalka@redhat.com> Date: Wed Dec 13 15:55:01 2023 -0500 c++: unifying FUNCTION_DECLs [PR93740] unify currently always returns success when unifying two FUNCTION_DECLs (due to the is_overloaded_fn deferment within the default case), which means for the below testcase we incorrectly unify &A::foo and &A::bar leading to deduction failure for the index_of calls due to a bogus base class ambiguity. This patch makes unify handle FUNCTION_DECL naturally like other decls. PR c++/93740 gcc/cp/ChangeLog: * pt.cc (unify) <case FUNCTION_DECL>: Handle it like FIELD_DECL and TEMPLATE_DECL. gcc/testsuite/ChangeLog: * g++.dg/template/ptrmem34.C: New test.
Fixed for GCC 14.
It seems like bug 104678 might be related to this