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:
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.