Назначить абстрактные функторы для 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
был просто подвижен, но я могу ошибаться там.