Назначить абстрактные функторы для std::function - почему std::ref является решением?

Я хотел бы инкапсулировать назначение функторов в std:: function в метод. Вместо передачи std:: function или указателя на std:: function я должен передать функторы, которые наследуются от общего абстрактного класса Slot (то есть эти слоты предоставляют дополнительную функциональность).

Я наткнулся на эту проблему в другой форме здесь. Например, мотивация использования указателей общих слотов вместо 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&)>’"

И компилируется, если используется версия A в assignFunctorPtr(...).

  • Почему он компилируется, если std:: ref используется для переноса функтора?
  • Следовательно, каковы конкретные требования std:: function для функтора (см. Также ссылку на std:: function)
  • Каков будет правильный / лучший способ решить эту проблему?
  • Сохраняется ли использование std:: ref?

1 ответ

Решение

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
}

Я считаю, что эта версия также будет работать, если SlotType был просто подвижен, но я могу ошибаться там.

Другие вопросы по тегам