I would like to specialize a template to do one thing on pointers to data members and another thing on pointers to member functions. This used to work up until gcc 11, with member functions acting as more specific. It still works with clang 11, but seems to have broken with gcc.
Here is a minimal non-working example:
#include <iostream>
template<auto F> struct which;
template<typename K, typename V, K V::*F>
struct which<F> {
static constexpr char desc[] = "pointer to data member";
};
template<typename K, typename V, K (V::*F)()>
struct which<F> {
static constexpr char desc[] = "pointer to member function";
};
struct S {
int i;
int f() { return 0; }
};
int
main()
{
std::cout << "S::i: " << which<&S::i>::desc << std::endl;
std::cout << "S::f: " << which<&S::f>::desc << std::endl;
}
As of gcc 11.1, compiling with g++ -std=c++17 memptr.cc
gives:
memptr.cc: In function 'int main()':
memptr.cc:24:40: error: ambiguous template instantiation for 'struct which<&S::f>'
24 | std::cout << "S::f: " << which<&S::f>::desc << std::endl;
| ^~
memptr.cc:6:8: note: candidates are: 'template<class K, class V, K V::* F> struct which<F> [with K = int(); V = S; K V::* F = &S::f]'
6 | struct which<F> {
| ^~~~~~~~
memptr.cc:11:8: note: 'template<class K, class V, K (V::* F)()> struct which<F> [with K = int; V = S; K (V::* F)() = &S::f]'
11 | struct which<F> {
| ^~~~~~~~
memptr.cc:24:42: error: incomplete type 'which<&S::f>' used in nested name specifier
24 | std::cout << "S::f: " << which<&S::f>::desc << std::endl;
| ^~~~
Is this a bug in gcc, or a bug in my code? Either way, what is the simplest workaround?