2013-04-30 6 views
4

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

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

Этот код ломается, если версия B используется в 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&)>’" 

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

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

ответ

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

+2

Шаблонная версия может быть улучшена путем использования 'SlotType && slot' и выполнения' f = std :: forward (slot); 'в теле, я думаю. Таким образом, вызывающий может выбрать, нужно ли перемещать или копировать в зависимости от того, что он передает, и больше никаких указателей. – David

+0

Спасибо за этот ответ. Я проверил ваше предложение, чтобы сделать assignFunctorPtr (...) шаблоном участника, и он компилируется отлично! – spinxz