I would like to pass functors of different types but identical signatures to a method. Hence I concluded that std::function should be used. But as this method should also store a reference to the function object I want to pass a shared_ptr instead (for lifetime management). The code below works for class B (b.run(...)) but fails to compile for class A (a.run(...) breaks). What is the reason for this conversion problem when a pointer is passed instead of the function object itself and how can I circumvent it?
#include <functional>
#include <memory>
class MyFunctor
{
public:
void operator()(const float &f)
{}
};
template<class FunSig>
class A
{
public:
void run(std::shared_ptr<std::function<FunSig> > f_ptr)
{
// store f_ptr in a vector
}
};
template<class FunSig>
class B
{
public:
void run(std::function<FunSig> f)
{}
};
int main()
{
MyFunctor mf1;
std::shared_ptr<MyFunctor> mf2_ptr(new MyFunctor);
A<void (const float &)> a;
B<void (const float &)> b;
a.run(mf2_ptr); // this breaks!
b.run(mf1); // this works
}
The compiler error:
error: no matching function for call to ‘A<void(const float&)>::run(std::shared_ptr<MyFunctor>&)’
note: candidate is:
note: void A<FunSig>::run(std::shared_ptr<std::function<FunSig> >) [with FunSig = void(const float&)]
note: no known conversion for argument 1 from ‘std::shared_ptr<MyFunctor>’ to ‘std::shared_ptr<std::function<void(const float&)> >
Now I discovered that a.run(...) compiles if MyFunctor inherits from std::function:
class MyFunctor : public std::function<void (const float &)>
Why does this work now? I would be nicer if no code changes were necessary in the functors.
Your question is equivalent to asking why this doesn't work:
struct Integer
{
int value;
};
std::shared_ptr<int> p(new int(1));
std::shared_ptr<Integer> p2 = p;
It doesn't work because they're not the same type. Just because you can store a MyFunctor
in a std::function<void(const float&)>
doesn't mean that a pointer to one is convertible to a pointer to the other.
You want:
auto mf2_ptr = std::make_shared<std::function<void (const float &)>>( MyFunctor() );
a.run(mf2_ptr);
Now I discovered that a.run(...) compiles if MyFunctor inherits from std::function:
It compiles because now you can convert shared_ptr<MyFunctor>
to shared_ptr<function<void(const float&)>>
, but it won't work correctly. std::function::operator()()
is not virtual, so if you call the function it will call the base class' operator()
, but the base class doesn't point to anything and will throw std::bad_cast
.