将抽象函子分配给std :: function - 为什么std :: ref是一个解决方案?

时间:2013-04-30 12:13:36

标签: c++ c++11 operator-overloading std-function

我想将函数的赋值封装到std :: function中。我没有传递std :: function或指向std :: function的指针,而是传递从公共抽象类Slot继承的函子(即这些插槽提供了额外的功能)。

我以不同的形状here偶然发现了这个问题。例如。在那里,使用通用槽指针而不是std:functions的动机是对仿函数的生命周期管理。

以下代码说明了该问题。请参阅assignFunctorPtr(...)方法。

#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);
}

如果在assignFunctorPtr(...)中使用版本B,则此代码会中断。

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

如果使用了assignFunctorPtr(...)中的版本A,它就会编译。

  • 如果使用std :: ref来包装函子,为什么要编译?
  • 因此,对于仿函数,std :: function的具体要求是什么(另请参阅std::function reference
  • 解决此问题的正确/最佳方法是什么?
  • 是否保存使用std :: ref?

1 个答案:

答案 0 :(得分:6)

std::function复制其论点。由于要分配的对象属于基本类型(并且具有纯虚拟成员函数),因此无法复制。请注意,如果它没有纯虚拟成员函数,则它可能是可复制的,但您会遇到object slicing

使用std::ref是安全的,只要您确保std::ref绑定到的对象的寿命超过所有引用它的对象。

在我看来,最优雅的解决方案是使assignFunctorPtr成为一个函数模板,它接受真实类型的仿函数(而不是基类型)的参数。如果这是可复制的,则分配将在没有std::ref的情况下起作用。

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

我相信如果SlotType只是可移动的话,这个版本也会起作用,但我可能错了。