2015-05-11 8 views
1

Цель: Наличие функции диспетчеризации, работающей по назначению. Минимальный примерный код должен говорить сам за себя. Я хочу поддерживать задачи: Именованная задача, реализованная в их собственном классе, и более простая задача, указанная с использованием лямбда. В идеале все, что может быть преобразовано в std::function<void (void)>, должно работать.std :: enable_if и std :: shared_ptr

#include <iostream> 
#include <memory> 
#include <functional> 

// A Base class for my tasks                                                     
class BaseTask 
{ 
public: 
    virtual void blah() = 0; 
}; 

// An important enough tasks that it gets to have its                                               
// own class.                                                         
class NamedTask : public BaseTask 
{ 
public: 
    virtual void blah() 
    { 
    std::cout << "Hey !" << std::endl; 
    } 
}; 

// A wrapper around simpler tasks. (lambda)                                                  
class GenericTask : public BaseTask 
{ 
public: 
    GenericTask(const std::function<void (void)> fct) : 
    fct_(fct) {} 

    virtual void blah() override 
    { 
    fct_(); 
    } 

private: 
    std::function<void (void)> fct_; 
}; 

void enqueue(std::shared_ptr<BaseTask> t) 
{ 
    // store in queue.                                                       
    // We'll just call it here for the sake of the example                                              
    t->blah(); 
} 

template<typename Callable> 
//typename std::enable_if<!std::is_base_of<BaseTask, Callable>::value>::type                                         
//typename std::enable_if<!std::is_base_of<std::shared_ptr<BaseTask>, Callable>::value>::type                                     
void enqueue(const Callable &c) 
{ 
    auto t = std::make_shared<GenericTask>(c); 
    t->blah(); 
} 

int main() 
{ 
    auto named = std::make_shared<NamedTask>(); 
    enqueue(named); // doesn't compile: tries to call the templated enqueue.                                         

    enqueue([]() -> bool { std::cout << "Lamda" << std::endl; }); 
} 

Проблема: Я не удается написать правильный enable_if шаблон. Прокомментированные строки в примере - это то, что я пробовал.

  • Первый не работает, потому что Callable имеет тип std::shared_ptr<NamedTask>, который не является дочерним BaseTask.
  • Второй тоже не удается, предположительно потому, что std::shared_ptr<NamedTask> не происходит от std::shared_ptr<BaseTask>.

ответ

3

У вас есть два случая: ваша вызываемый является конвертируемыми в shared_ptr<BaseTask>, или нет. Проверка базы неверна, так как shared_ptr<NamedTask> не связан иерархией классов с shared_ptr<BaseTask>, но вы можете построить из нее shared_ptr<BaseTask>.

То есть:

// is convertible to shared_ptr<BaseTask> 
void enqueue(std::shared_ptr<BaseTask> t); 

// is NOT convertible to shared_ptr<BaseTask> 
template<typename Callable> 
typename std::enable_if< 
    !std::is_convertible<Callable, std::shared_ptr<BaseTask>>::value 
>::type 
enqueue(const Callable &c); 

В качестве альтернативы, вы можете думать о двух случаях, как конструктивно/не построимых:

template<typename Callable> 
typename std::enable_if< 
    !std::is_constructible<std::shared_ptr<BaseTask>, Callable>::value 
>::type 
enqueue(const Callable &c); 

Третьей альтернатива кондиционировать шаблон функции enqueue на что-либо, что является вызываемым с нулевыми аргументами:

template<typename Callable> 
auto enqueue(const Callable &c) -> decltype(c(), void()) 
+0

Я не знал о 'std :: is_convertible'. Это именно то, что я искал. Благодарим вас за предоставление жизнеспособных альтернатив. – Xaqq

Смежные вопросы