2014-01-03 3 views
2

Я пытаюсь написать симуляцию, управляемую событиями, в C++. Сейчас это просто скелетное приоритет очереди unique_ptrs до базового класса Event:Изменение priority_queue unique_ptrs

class Event 
{ 
public: 
    double time; 
    Event(double time); 
    virtual void handle() = 0; 
}; 

struct EventCompare 
{ 
    bool operator()(std::unique_ptr<Event> e1, std::unique_ptr<Event> e2) { 
    return e1->time > e2->time; 
    } 
}; 

class DumpSimulationEvent : public Event 
{ 
public: 
    DumpSimulationEvent(const double time); 
    void handle(); 
}; 

typedef std::priority_queue<std::unique_ptr<Event>, std::vector<std::unique_ptr<Event>>, EventCompare> EventQueue; 

class Simulation 
{ 
    double time; 
    EventQueue eventQueue; 
public: 
    Simulation(); 
    void run(); 
}; 

Event::Event(const double t) 
{ 
    time = t; 
} 

DumpSimulationEvent::DumpSimulationEvent(const double t) : Event(t) 
{ 
} 

void DumpSimulationEvent::handle() 
{ 
    std::cout << "Event time: " << time; 
} 

Simulation::Simulation() 
{ 
    time = 0; 
    eventQueue = EventQueue(); 
    std::unique_ptr<DumpSimulationEvent> dumpEvent5(new DumpSimulationEvent(5)); 
    //eventQueue.emplace(dumpEvent5); 
} 

void Simulation::run() 
{ 
    while (!eventQueue.empty()) { 
     std::unique_ptr<Event> currentEvent = std::move(eventQueue.top()); 
     //eventQueue.pop(); 
     time += currentEvent->time; 
     currentEvent->handle(); 
    } 
} 

Основная функция (не показано выше) просто создает экземпляр моделирования и вызывает метод запуска(). Проблема заключается в том, что раскомментировав либо устанавливать() или поп() результаты в

error C2280: 'std::unique_ptr<Event,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : attempting to reference a deleted function c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility 521 1 

Исследования показывают, что наиболее вероятной причиной является попытка скопировать unique_ptr. Тем не менее, я теряю, есть ли это фактическая причина и действительно ли это происходит в комментариях или просто становится видимым там. Добавление аргумента std :: move to emplace не помогает.

+3

'станд :: ход (eventQueue.top());' это не получится, как 'priority_queue' только обеспечивает константный доступ к 'top'. См. Http://stackoverflow.com/q/20149471/420683 – dyp

+0

Не причина ошибки, которую вы видите, но класс 'Event' должен иметь деструктор' virtual', иначе UB будет вставлять объекты 'DumpSimulationEvent' в 'unique_ptr ' – Praetorian

+0

Определяет ли определение виртуального деструктора, я должен явно определять деструкторы и перемещать конструкторы для производных классов? – user3157796

ответ

3

Ваша проблема в том, что вы не правильно двигаете, но пытаетесь сделать копии в нескольких местах.

Вот разница, что делает ваш код работает, с некоторыми комментариями:

struct EventCompare 
{ 
- bool operator()(std::unique_ptr<Event> e1, std::unique_ptr<Event> e2) { 
+ bool operator()(std::unique_ptr<Event> const &e1, std::unique_ptr<Event> const &e2) { 
    return e1->time > e2->time; 
    } 
}; 

Здесь, как и juanchopanza упомянул в своем ответе, вы должны принять std::unique_ptr сек по ссылке, а не по значению, в противном случае вы попросив компилятор сделать копии для вас, что запрещено.

 time = 0; 
    eventQueue = EventQueue(); 
    std::unique_ptr<DumpSimulationEvent> dumpEvent5(new DumpSimulationEvent(5)); 
- //eventQueue.emplace(dumpEvent5); 
+ eventQueue.emplace(std::move(dumpEvent5)); 
} 

В приведенном выше коде, вы должны переместить свой std::unique_ptr в очереди. Emplace не волшебным образом перемещает вещи, а просто переводит аргументы в конструктор. Без std::move здесь, вы просите сделать копию . Вы могли бы просто написать: eventQueue.emplace(new DumpSimulationEvent(5)); и пропустить промежуточный объект.

 while (!eventQueue.empty()) { 
-  std::unique_ptr<Event> currentEvent = std::move(eventQueue.top()); 
-  //eventQueue.pop(); 
+  std::unique_ptr<Event> currentEvent(std::move(const_cast<std::unique_ptr<Event>&>(eventQueu 
+  eventQueue.pop(); 
     time += currentEvent->time; 
     currentEvent->handle(); 

Наконец, в приведенном выше коде, вы пытаетесь перейти от eventQueue.top(), но вы не можете перейти от const ссылки, которая является то, что top() возвращается. Если вы хотите заставить движение работать, вы должны использовать как const_cast, так и std::move(), как показано выше.

Вот полный измененный код, который отлично компилируется здесь с g++-4.8 -std=c++11:

#include <memory> 
#include <queue> 
#include <iostream> 

class Event 
{ 
public: 
    double time; 
    Event(double time); 
    virtual void handle() = 0; 
}; 

struct EventCompare 
{ 
    bool operator()(std::unique_ptr<Event> const &e1, std::unique_ptr<Event> const &e2) { 
    return e1->time > e2->time; 
    } 
}; 

class DumpSimulationEvent : public Event 
{ 
public: 
    DumpSimulationEvent(const double time); 
    void handle(); 
}; 

typedef std::priority_queue<std::unique_ptr<Event>, std::vector<std::unique_ptr<Event>>, EventCompare> EventQueue; 

class Simulation 
{ 
    double time; 
    EventQueue eventQueue; 
public: 
    Simulation(); 
    void run(); 
}; 

Event::Event(const double t) 
{ 
    time = t; 
} 

DumpSimulationEvent::DumpSimulationEvent(const double t) : Event(t) 
{ 
} 

void DumpSimulationEvent::handle() 
{ 
    std::cout << "Event time: " << time; 
} 

Simulation::Simulation() 
{ 
    time = 0; 
    eventQueue = EventQueue(); 
    std::unique_ptr<DumpSimulationEvent> dumpEvent5(new DumpSimulationEvent(5)); 
    eventQueue.emplace(std::move(dumpEvent5)); 
} 

void Simulation::run() 
{ 
    while (!eventQueue.empty()) { 
     std::unique_ptr<Event> currentEvent(std::move(const_cast<std::unique_ptr<Event>&>(eventQueue.top()))); 
     eventQueue.pop(); 
     time += currentEvent->time; 
     currentEvent->handle(); 
    } 
} 
+0

Спасибо, это сработало. Интересно, что он работал без обходного пути top() const_reference. Я предполагаю, что проблема покажет себя, когда в очереди или в очереди будет много событий? – user3157796

+0

@ user3157796 Вы говорите, что получили его для компиляции, переходя от ссылки 'const'? Если это так, возможно, это ошибка компилятора или библиотеки, поскольку константа 'const'' std :: unique_ptr' определена как неподвижная. В любом случае, сохранение 'const_cast' необходимо (или должно быть), но вы можете использовать operator = вместо конструктора для перемещения. (Я изменил его на конструктор, когда редактировал код, но это было не обязательно). – wjl

+0

Да, он работал только с добавлением std :: move to emplace и сменой компаратора на ссылки в соответствии с juanchopanza (его ответ по какой-то причине), сохраняя std :: move (eventQueue.top()) как есть.Компилятор, о котором идет речь, - MSVC 18.00.21005.1. http://www.cplusplus.com/reference/queue/priority_queue/top/ показывает const_reference как тип возврата для C++ 11, а не const value_type &. Может быть, это оболочка, которая может обрабатывать движение? – user3157796

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