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

spi*_*nxz 4 c++ operator-overloading c++11 std-function

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

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

以下代码说明了该问题.请参阅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);
}
Run Code Online (Sandbox Code Playgroud)

如果在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&)>’"
Run Code Online (Sandbox Code Playgroud)

如果使用assignFunctorPtr(...)中的版本A,它将编译.

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

Bjö*_*lex 6

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

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

在我看来,最优雅的解决方案是创建assignFunctorPtr一个函数模板,它接受真实类型的仿函数的参数(而不是基类型).如果这是可复制的,那么分配将无效std::ref.

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

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

  • 模仿的版本可以通过采用`SlotType && slot`并执行`f = std :: forward <SlotType>(slot);`来体现.这样,调用者可以根据他传入的内容选择是移动还是复制,并且不再涉及指针. (2认同)