Search code examples
c++templatesoverloadingpointer-to-memberoverload-resolution

Compiler is unable to resolve a matching class method passed via std::mem_fn


Consider the following code:

struct A {
    int& getAttribute();
    const int& getAttribute() const;
};

std::vector<int> foo(const std::vector<A>& as) {
    std::vector<int> ints;
    std::transform(as.begin(), as.end(), std::back_inserter(ints),
                   std::mem_fn(&A::getAttribute));
    return ints;
}

Its compilation (g++ -std=c++14 -c mem_fn.cpp, g++ version 7.5.0) fails with the following error:

error: no matching function for call to
‘mem_fn(<unresolved overloaded function type>)’

However,

  • If we keep only the const int& getAttribute() const method, the compilation succeeds
  • If we keep only the int& getAttribute() method, the compilation fails with the following error messages:
    /usr/include/c++/7/bits/stl_algo.h:4306:24:
         error: no match for call to
         ‘(std::_Mem_fn<int& (A::*)()>) (const A&)’
    /usr/include/c++/7/functional:174:27:
         error: no matching function for call to
         ‘__invoke(int& (A::* const&)(), const A&)’
    /usr/include/c++/7/bits/invoke.h:89:5:
         error: no type named ‘type’ in
         ‘struct std::__invoke_result<int& (A::* const&)(), const A&>’
    

Alternatively, we could use a lambda here or help the compiler by explicitly specifying the type of a matching method as mem_fn's template argument: std::mem_fn<const int& () const>(&A::getAttribute).

So, it looks like, as the int& getAttribute() method does not fit, the const int& getAttribute() const method is the one that should have been choosen by the by the compiler in the original code. Why does compiler fail to select it and report the <unresolved overloaded function type> error?


Solution

  • The compiler should choose pointer of the function at the moment of mem_fn creation. It can only look at the std::mem_fn(&A::getAttribute) expression, but cannot look up how it's used. It's possible to work around with user-defined conversion operators, but is generally not done.

    So, your reasoning about future uses of this mem_fn does not apply.

    You have to specify the exact overload of &A::getAttribute to use. static_cast to a fixed type will work (it's a special case where unresolved overloaded functions are permitted):

      std::transform(
          as.begin(), as.end(), std::back_inserter(ints),
          std::mem_fn(static_cast<const int &(A::*)() const>(&A::getAttribute)));