Question

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.

Était-ce utile?

La solution

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.

Autres conseils

I'm not quite understanding why you want a reference to the std::function object. Unless you really want shared reference semantics (for example, the ability for someone else to modify what function object is being used), just store a std::function object directly.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top