#include <utility>
#include<unordered_map>
#include<queue>
using namespace std;
struct Task {
char t;
int cnt;
Task(char ch, int cnt):t(ch), cnt(cnt){};
};
struct CompTask {
bool operator() (const unique_ptr<Task>& a, const unique_ptr<Task>& b) {
return a->cnt < b->cnt;
}
};
class Schedule {
public:
int schedule() {
unordered_map<unique_ptr<Task>, int> sleep_q;
priority_queue<unique_ptr<Task>, vector<unique_ptr<Task>>, CompTask> ready_q; // max heap
ready_q.push(std::make_unique<Task>('A', 1));
auto& ptr = ready_q.top();
//sleep_q.insert({ptr, 1}); // compile error
sleep_q.insert({std::move(ptr), 1}); // compile error
// some other code...
return 1;
}
};
int main() {
return 0;
}
// error:
cpp:38:17: error: no matching member function for call to 'insert'
sleep_q.insert({std::move(ptr), 1}); // compile error
~~~~~~~~^~~~~~
Контекст программирования:
У меня был класс задач, и программа пытается имитировать планирование задач (которое включает перемещение задачи туда и обратно между очередью готовности и очередью ожидания).
У меня есть два стандартных контейнера для очереди готовности и очереди ожидания соответственно, priority_queue имеет тип значения unique_ptr<Task>
, другой
unorder_map (спящая очередь), ключ которой также unique_ptr<Task>
. У меня возникли проблемы с перемещением объекта unique_ptr из Priorty_queue в unordered_map (показано в коде).
Мои вопросы:
(1) как вставить элемент в unordered_map, при этом у меня были ошибки компиляции.
(2) в контексте проблемы, какой тип «указателя» предпочтительнее? unique_ptr<Task>, shared_ptr<Task>, or just Task*
@PaulMcKenzie, хеш-функтор по умолчанию поддерживает unique_ptr:en.cppreference.com/w/cpp/utility/hash. По вашему предложению я добавил информацию об ошибке компиляции.
Для (1) вы пробовали emplace
вместо insert
? Смотрите это
@PaulMcKenzie пытался, но все равно с ошибкой.
Чтобы переместить unique_ptr
между стандартными контейнерами, необходимо уничтожить один unique_ptr
(хранящийся в первом стандартном контейнере) и передать резервные данные в новый unique_ptr
. Это можно сделать с помощью std::move
. Однако обычно проще использовать shared_ptr
. Это позволяет избежать проблемы, позволяя shared_ptr
указывать на одну и ту же задачу одновременно в двух стандартных контейнерах. Хотя бы на мгновение.
Однако, поскольку у вас может быть несколько объектов shared_ptr
, указывающих на одну и ту же задачу, shared_ptr
не будет работать для однозначной идентификации задачи. Для этого я бы рекомендовал создать идентификатор задачи для каждой задачи. Для этого подойдет простое целое число, если вы гарантируете, что оно уникально для этой задачи. Целочисленный идентификатор задачи будет хорошо работать с картой, поскольку идентификатор можно использовать в качестве ключа.
Кажется, не хватает функции перемещения из std::priority_queue<>
. Вы можете обойти это, используя const_cast
, хотя в редких случаях это может привести к неопределенному поведению (когда тип сохраненного элемента — const
).
int schedule() {
unordered_map<unique_ptr<Task>, int> sleep_q;
priority_queue<unique_ptr<Task>, vector<unique_ptr<Task>>, CompTask> ready_q; // max heap
ready_q.push(std::make_unique<Task>('A', 1));
unique_ptr<Task> ptr =
std::move(const_cast<unique_ptr<Task>&>(ready_q.top()));
// ^ Here. priority_queue::top() returns `const&`.
ready_q.pop(); // remove moved element from priority_queue
sleep_q.insert(std::make_pair(std::move(ptr), 1));
// some other code...
return 1;
}
Ссылка: Этот ответ
Спасибо, что указали на корень ошибки. Кажется, мы должны поставить ready_q.pop()
после sleep_q.insert(std::make_pair(std::move(ptr), 1));
, иначе будет ошибка времени выполнения. Вы заметили это?
@ kz28 О, ты прав. Сохранение ссылки на элемент, который затем извлекается, является неопределенным поведением. Теперь это должно работать. Элемент unique_ptr
теперь перемещается непосредственно в ptr
, а затем извлекается.
@ kz28 Обратите внимание на еще одно изменение. std::move
более идиоматичен, чем использование const_cast
для изменения ссылки lvalue на rvalue.
Укажите ошибки компилятора.
unordered_map
требует, чтобы ключ можно было хэшировать, если нет, вы должны предоставить хеш-функцию в качестве аргумента шаблона.