Как переместить объект unique_ptr между двумя контейнерами STL

#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*

Укажите ошибки компилятора. unordered_map требует, чтобы ключ можно было хэшировать, если нет, вы должны предоставить хеш-функцию в качестве аргумента шаблона.

PaulMcKenzie 13.12.2020 06:09

@PaulMcKenzie, хеш-функтор по умолчанию поддерживает unique_ptr:en.cppreference.com/w/cpp/utility/hash. По вашему предложению я добавил информацию об ошибке компиляции.

kz28 13.12.2020 06:17

Для (1) вы пробовали emplace вместо insert? Смотрите это

PaulMcKenzie 13.12.2020 06:45

@PaulMcKenzie пытался, но все равно с ошибкой.

kz28 13.12.2020 06:52
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
4
608
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Чтобы переместить 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 13.12.2020 18:02

@ kz28 О, ты прав. Сохранение ссылки на элемент, который затем извлекается, является неопределенным поведением. Теперь это должно работать. Элемент unique_ptr теперь перемещается непосредственно в ptr, а затем извлекается.

Kostas 13.12.2020 18:36

@ kz28 Обратите внимание на еще одно изменение. std::move более идиоматичен, чем использование const_cast для изменения ссылки lvalue на rvalue.

Kostas 13.12.2020 18:40

Другие вопросы по теме