Pergunta

I would like to encapsulate the assignment of functors to a std::function into a method. Instead of passing a std::function or a pointer to a std::function I have to pass functors that inherit from a common abstract class Slot (i.e. these slots provide additional functionality).

I stumbled upon this problem in a different shape here. E.g. there, the motivation of using pointers of generic slots instead of std:functions is lifetime management of the functors.

The following code illustrates the problem. See the assignFunctorPtr(...) method.

#include <iostream>
#include <functional>

template<class FunSig>
class Slot;

template<class R>
class Slot<R()>
{
public:
    typedef R Ret_type;

public:
    virtual ~Slot() {}
    virtual Ret_type operator()() = 0;
};

template<class R, class A1>
class Slot<R(A1)>
{
public:
    typedef R Ret_type;
    typedef A1 Arg1_type;

public:
    virtual ~Slot() {}
    virtual Ret_type operator()(Arg1_type) = 0;
};

class TestSlot: public Slot<void (float &)>
{
public:
    void operator()(float& f)
    { std::cout << f ;}
};


template<class FunSig>
class TestSignal
{
public:
    typedef Slot<FunSig> Slot_type;

    std::function<FunSig> f;

    void assignFunctorPtr(Slot_type* slot_ptr)
    {
        //f = std::ref(*slot_ptr);   // A -> works!
        f = *slot_ptr;               // B -> compiler error!
    }
};


int main()
{
    TestSlot* slot = new TestSlot;
    TestSignal<void (float &)>* signal = new TestSignal<void (float &)>;

    signal->assignFunctorPtr(slot);
}

This code breaks if version B is used in assignFunctorPtr(...).

Error: "error: cannot allocate an object of abstract type ‘Slot<void(float&)>’
note:   because the following virtual functions are pure within ‘Slot<void(float&)>’"

And it compiles if version A in assignFunctorPtr(...) is used.

  • Why does it compile if std::ref is used to wrap the functor?
  • Hence what are the specific requirements of std::function for a functor (see also std::function reference)
  • What would be the correct/best way to solve this problem?
  • Is it save to use std::ref?
Foi útil?

Solução

std::function copies its arguments. Since the object you want to assign is of the base type (and has a pure virtual member function), it cannot be copied. Note that, if it didn't have a pure virtual member function, it might be copyable, but you would suffer from object slicing.

Using std::ref is safe as long as you make sure that the object to which std::ref is bound lives longer that all references to it.

The most elegant solution, in my opinion, would be to make assignFunctorPtr a function-template that takes an argument of the real type of functor (as opposed to a base-type). If this is copyable, the assignment would work without std::ref.

template<class SlotType>
void assignFunctorPtr(SlotType* slot_ptr)
{
    f = *slot_ptr;               // works if SlotType is copyable
}

I believe this version would also work if SlotType was just movable, but I might be wrong there.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top