Search code examples
c++templatesvisual-c++clangtemplate-matching

clang template matching cannot match pointer to method inherited from base class


The following code does not compile with Clang (but compiles fine with MSVC):

class Base {
public:
    int bla() { return 1; }
};

class Derived : public Base {
};

template <typename T>
void func(T (Derived::*method)()) {
}

int main() {
  func(&Derived::bla);
}

With the error:

<source>:19:3: error: no matching function for call to 'func'
   19 |   func(&Derived::bla);
      |   ^~~~
<source>:13:6: note: candidate template ignored: could not match 'Derived' against 'Base'
   13 | void func(T (Derived::*method)())
      |      ^

See on Compiler Explorer.

Which compiler is correct W.R.T. the standard?


Solution

  • Clang is correct. The type of the expression &Derived::bla is int (Base::*)(), not int (Derived::*)(), and Base in that type doesn't match Derived in your template.

    If you want the expression to have type int (Derived::*)() you must cast it:

    func(static_cast<int (Derived::*)()>(&Derived::bla));
    

    But probably it would be better to replace Derived in the template by a template parameter and then to constrain it to be identical to or a base class of Derived.

    Consider whether you really need a specific pointer-to-member as function argument at all. Generally it would be sufficient to allow any type and then constrain on std::invoke(method, std::declval<Dervived*>()) being well-formed. Then it would work without cast and a user could pass a lambda or anything else suitable as well.