Использование std :: move - PullRequest
3 голосов
/ 14 июля 2020

Ниже в вопросе - упрощенная версия кода для SO. Исходный код:

  • Получает структуру, которую нельзя изменять или делать недействительной
  • Создает другую структуру, в которой некоторые данные скопированы из исходной структуры
  • Новый Созданная структура не может быть изменена, все ее члены: const
  • Вновь созданная структура не может иметь конструктор, который знает об исходной структуре

Упрощенный код для SO не у меня нет исходной структуры, я просто составил данные с помощью циклов. Однако важная деталь заключается в том, что конструктору новой структуры будет передано std::vector. Они заполняются в циклах и перемещаются в новую структуру

Мне пришло в голову, что std::vector не понадобится после создания новой структуры, поэтому std::move будет подходящим. В приведенном ниже коде я обозначил использование как «Перемещение 1», «Перемещение 2», «Перемещение 3» и «Перемещение 4»:

#include <iostream>
#include <vector>

struct A {
    const int m_a;

    A() = delete;
    A(const int a_a) : m_a(a_a) {}

    void display() const { std::cout << "    " << m_a << "\n"; }
};

struct B {
    const std::vector<A> m_as;
    const int            m_b;

    B() = delete;
    B(
        std::vector<A>&& a_as,
        const int        a_b
    ) :
        m_as(std::move(a_as)), // Move 1
        m_b(a_b)
    {}

    void display() const {
        std::cout << "  " << m_b << ":\n";
        for (const A& a : m_as)
            a.display();
    }
};

struct C {
    const std::vector<B> m_bs1;
    const std::vector<B> m_bs2;

    C() = delete;
    C(
        std::vector<B>&& a_bs1,
        std::vector<B>&& a_bs2
    ) :
        m_bs1(std::move(a_bs1)), // Move 2
        m_bs2(std::move(a_bs2))  // Move 2
    {}

    void display() const {
        std::cout << "0:\n";
        for (const B& b : m_bs1)
            b.display();
        std::cout << "1:\n";
        for (const B& b : m_bs2)
            b.display();
    }
};

int main() {
    // Manually making up data, actual usage will take data from a different
    // kind of structure and populate vectors
    std::vector<B> bs1, bs2;
    for (int i = 0; i < 3; ++i) {
        std::vector<A> as;
        for (int j = 0; j < 2; ++j)
            as.emplace_back(j);
        bs1.emplace_back(std::move(as), i); // Move 3
    }
    for (int i = 0; i < 3; ++i) {
        std::vector<A> as;
        for (int j = 0; j < 2; ++j)
            as.emplace_back(j);
        bs2.emplace_back(std::move(as), i); // Move 3
    }

    C c(std::move(bs1), std::move(bs2)); // Move 4
    c.display();
    return 0;
}

Некоторые предположения :

  • Я считаю, что нет значительной разницы между «Move 1» и «Move 2»
  • Я считаю, что нет значительной разницы между «Move 3» и «Move 4»
  • Насколько мне известно, std::forward не является хорошей заменой для любого из вариантов использования std::move, потому что нет шаблонов

Вопрос : Все ли std::move использования значимы или какие-либо из них не нужны?

1 Ответ

4 голосов
/ 14 июля 2020

Вы можете увидеть, что большая часть std::move требуется, удалив их, ваш код не будет компилироваться. Единственными исключениями являются Move 1 и Move 2, код по-прежнему будет компилироваться без них, но только потому, что вместо него вызывается конструктор копирования std::vector.

Если вы хотите перейти от объекта, который вам почти всегда нужен для использования std::move, единственные исключения - при переходе из временного и при возврате объекта.

...