Можно ли "переместить" объект из очереди, если вы собираетесь выскочить из него?

Я работал над синтаксическим анализатором для commands (которые представляют собой причудливые обертки вокруг больших массивов данных) и имеет очередь, в которой находятся необработанные команды. Если мне нужна команда, я запрашиваю ее с кодом следующим образом:

boost::optional<command> get_command() {
    if (!has_command()) return boost::optional<command>(nullptr);
    else {
        boost::optional<command> comm(command_feed.front()); //command_feed is declared as a std::queue<command>
        command_feed.pop();
        return comm;
    }
}

Проблема в том, что эти команды могут быть мегабайтами в размере, при правильных обстоятельствах, и нужно разбираться довольно быстро. Моя мысль заключалась в том, что я мог бы оптимизировать передачу таким образом:

boost::optional<command> get_command() {
    if (!has_command()) return boost::optional<command>(nullptr);
    else {
        boost::optional<command> comm(std::move(command_feed.front())); //command_feed is declared as a std::queue<command>
        command_feed.pop();
        return comm;
    }
}

И, похоже, это работает для этого конкретного случая, но может ли это использоваться как решение общего назначения для любого правильно сохраненного объекта RAII, или я должен делать что-то еще?

Ответ 1

Да, это совершенно безопасно:

std::queue<T> q;
// add stuff...

T top = std::move(q.front());
q.pop();

pop() не имеет каких-либо предварительных условий для первого элемента в q, имеющего указанное состояние, и поскольку вы не используете впоследствии q.front(), вам не нужно иметь дело с тем, что этот объект недействителен Больше.

Звучит неплохо!

Ответ 2

Это зависит от того, что делает конструктор перемещения для вашего типа. Если он оставляет исходный объект в состоянии, которое можно безопасно уничтожить, тогда все будет хорошо. Если нет, тогда у вас могут быть проблемы. Обратите внимание, что комментарии о предварительных условиях и действительных состояниях связаны с ограничениями на типы, определенные в стандартной библиотеке. Типы, которые вы определяете, не имеют этих ограничений, за исключением того, что они используют типы из стандартной библиотеки. Поэтому посмотрите на свой конструктор перемещения, чтобы разобраться, что вы можете и чего не можете сделать с перемещенным объектом.

Ответ 3

Да. Пока ваш аргумент шаблона контейнера std::queue гарантирует отсутствие предварительных условий для состояния его содержащихся значений для pop_front(); значение по умолчанию для std::queue равно std::deque и предоставляет гарантию.

Пока вы обеспечиваете то, что я написал в предыдущем абзаце, вы полностью в безопасности. Вы собираетесь удалить этот элемент из своей очереди, поэтому нет причин не переносить его, поскольку вы берете на себя ответственность за этот объект.

Ответ 4

перемещение объекта может привести к его недействительности. Его инварианты больше не гарантируются. Вы были бы безопасны выталкивать его из очереди без вмешательства.

  • std:: move сам по себе ничего не говорит о компиляторе, что он может выбрать комманду, которая принимает значение r.

  • Хорошо написанная комманда, затем украдет представление из старого объекта для нового объекта. Например, просто скопируйте указатели на новый объект и обнулите указатели в старом объекте (таким образом, деструктор старого объекта не уничтожит массивы).

  • Если comm не перегружен, чтобы сделать это, не будет никакой пользы для std:: mov.