Безопасно ли двигаться из верхней / передней части стека / очереди STL? - PullRequest
3 голосов
/ 27 апреля 2020

Вопрос применим как к стекам, так и к очередям, но я просто упомяну здесь стек для простоты.

Предполагая, что мы помещаем sh неконстантные объекты в std::stack, это безопасно, когда мы выталкиваем из стека, перед перемещением объекта в верхней части стека во временную переменную, как показано ниже:

std::stack<std::string> st;
st.emplace("asdf");
auto popped = std::move(st.top());
st.pop(); 

1 Ответ

7 голосов
/ 28 апреля 2020

Да, это безопасно, если вы используете неконстантную версию стека. (Для константной версии стека вы, скорее всего, скопируете объект или получите ошибку компиляции)

Поскольку std::stack возвращает неконстантную ссылку на объект, вы можете изменить его. Это включает в себя перемещение от объекта.

Напомним, что перемещение от объекта сохраняет его в допустимом состоянии (по крайней мере, если класс реализован правильно). Это не уничтожает его. Это все еще там на вершине вашего стека. Это просто не содержит никакой определенной ценности. Последующие действия pop без проблем вызовут для него надлежащий деструктор.

Обратите внимание, что вывоз не разрешен для std::priority_queue, поскольку контейнер такого типа на самом деле заботится о содержании. И - по этой причине он просто возвращает константную ссылку, которая не может быть использована для удаления вещей из нее.


В ответ на наблюдение Яманона о том, что std::move может быть выполнено на константной ссылке. На самом деле вы можете сделать это. Обычно это бесполезно, хотя обычно сокращается до копии вместо перемещения. Рассмотрим следующий пример:

#include <iostream>
#include <string>
#include <stack>

class Foo {
    public:
    Foo() {
        std::cout << "created\n";
        }
    ~Foo() {
        std::cout << "destroyed  " << value << "\n";
    }
    Foo(const Foo& other) : value(other.value) {
        std::cout << "copied\n";
    }
    Foo(Foo&& other) : value(other.value) {
        other.value++;
        std::cout << "moved\n";
    }
    Foo(const Foo&& other) : value(other.value) {
        std::cout << "const-moved\n";
    }    
    int value = 0;
};

int main()
{
std::stack<Foo> st;
st.emplace();
const std::stack<Foo>& cst = st;
auto popped = std::move(cst.top());
st.pop();
}

Если вы запустите вышеизложенное, будет использована версия с перемещением const. Однако, реализовав конструктор const-move, вы поймете, что на самом деле вы не можете добиться большего успеха, чем ваш обычный конструктор копирования. Это потому, что other остаются неизменными. Например, вы не можете получить право собственности на что-либо other и сбросить его в пределах other.

И если вы удалите конструктор const-move, компилятор будет использовать обычный конструктор копирования.

Если конструктор копирования удален и предоставляется только конструктор перемещения - , тогда компилятор выдаст вам ошибку.

...