Search code examples
c++staticassignment-operatorstd-function

no viable overloaded '=' for overloaded static member functions


I have this simplified code consisting of a class with a static function, which is stored in map:

#include <iostream>
#include <functional>
#include <map>

class A {
public:
  static void f(const std::string &s) { std::cout << s; }
}; 

std::map<std::string, std::function<void(std::string const &)>> fs; 

int main() {
  fs["f"] = &A::f;
  fs["f"]("hello");
}

This prints the expected hello.

The problem occurs if I overload f() with:

 static void f(const std::string &s, int c) { while(c-->0) { std::cout << s; } }

This results in the error:

 error: no viable overloaded '='
   fs["f"] = &A::f;
   ~~~~~~~ ^ ~~~~~
 /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2241:7: note: candidate function not viable: no overload of 'f' matching 'const std::function<void (const std::basic_string<char> &)>' for 1st argument
       operator=(const function& __x)
       ^
 /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2259:7: note: candidate function not viable: no overload of 'f' matching 'std::function<void (const std::basic_string<char> &)>' for 1st argument
       operator=(function&& __x)
       ^
 /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2273:7: note: candidate function not viable: no overload of 'f' matching 'nullptr_t' for 1st argument
       operator=(nullptr_t)
       ^
 /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2302:2: note: candidate template ignored: couldn't infer template argument '_Functor'
       operator=(_Functor&& __f)
       ^
 /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2311:2: note: candidate template ignored: couldn't infer template argument '_Functor'
       operator=(reference_wrapper<_Functor> __f) noexcept
       ^

However, calling both functions works:

A::f("hello ");
A::f("echo ", 3);

So, my question are:

  1. Why this code not compiling even though the operator= seems to exist and function if I don't overload f()?
  2. How can I get it to work without giving both functions different names?

Solution

  • Why this code not compiling even though the operator= seems to exist and function if I don't overload f()?

    Because the compiler doesn't know which overload to choose. How could he? There is no criterion upon which he can decide which one is suited better. Every std::function allows arbitrary function objects to be assigned and doesn't check any signatures. If you wanted to save only function pointers of this particular signature you should have declared the map appropriately.

    How can I get it to work without giving both functions different names?

    As already mentioned it works by casting the expression to a function pointer of the specific type.

    fs["f"] = static_cast<void(*)(std::string const&)>( &A::f );
    

    This way no ambiguities arise; There is exactly one overload that can be casted to this function to pointer type.
    If this appears more often then a typedef could be feasible.

    Or a little helper class template:

    template <typename... Exact>
    struct funptr
    {
        template <typename R>
        constexpr auto operator()(R(*p)(Exact...)) -> decltype(p)
        { return p; }
    };
    
    fs["f"] = funptr<std::string const&>()(&A::f);
    

    Demo.