Я возился с движением c торсами для обучения / повторения sh упражнения, и я наткнулся на кое-что неожиданное для меня. Ниже у меня есть класс person
, содержащий std::string m_name;
. Я использую это как тестовый класс для копирования / перемещения c 'торсов.
Вот код для быстрой справки:
#include <iostream>
#include <vector>
class person
{
public:
std::string m_name;
explicit person(const std::string &name) : m_name(name)
{
std::cout << "created " << m_name << std::endl;
}
~person()
{
std::cout << "destroyed " << m_name << std::endl;
}
person(const person &other) : m_name(other.m_name)
{
m_name += ".copied";
std::cout << "copied " << other.m_name << " -> " << m_name << std::endl;
}
person(const person &&other) noexcept : m_name(std::move(other.m_name))
{
m_name += ".moved";
std::cout << "moved " << other.m_name << " -> " << m_name << std::endl;
}
};
int main()
{
std::vector<person> people;
people.reserve(10);
std::cout << "\ncopy bob (lvalue):" << std::endl;
person bob{"bob"};
people.push_back(bob);
std::cout << "\nmove fred (lvalue):" << std::endl;
person fred{"fred"};
people.push_back(std::move(fred));
std::cout << "\ntemp joe (rvalue):" << std::endl;
people.push_back(person{"joe"});
std::cout << "\nterminating:" << std::endl;
}
Это дает мне результат, которого я ожидал (в основном, за исключением того, почему содержимое std :: string не «перемещается»?): https://godbolt.org/z/-J_56i
Затем я удаляю std :: vector reserve
, чтобы std :: vector должен «расти» по мере добавления элементов. Теперь я получаю то, чего действительно не ожидаю: https://godbolt.org/z/rS6-mj
Теперь я вижу, что bob копируется, а затем перемещается, когда добавляется fred, и затем перемещается снова, когда добавляется joe . У меня создалось впечатление, что std :: vector «перемещается», когда ему нужно перераспределить пространство. Но я думал, что он копирует / перемещает память, а не копирует / перемещает объект за объектом. Я действительно не ожидал, что он вызовет конструктор перемещения.
Теперь, если я удалю перемещение c 'tor, я обнаружу, что bob копируется три раза !: https://godbolt.org/z/_BxnvU Это кажется действительно неэффективным.
С cplusplus.com :
push_back ()
Добавить элемент в конце Добавляет новый элемент в конец вектора после его текущего последнего элемента. Содержимое val копируется (или перемещается) в новый элемент.
Это эффективно увеличивает размер контейнера на единицу, что вызывает автоматическое c перераспределение выделенного пространства хранения, если - и только если - размер нового вектора превосходит текущий размер вектора.
resize ()
Изменяет размер контейнера так, чтобы он содержал n элементов.
Если n меньше текущего размера контейнера, содержимое уменьшается до первых n элементов, удаляя те, что находятся за пределами контейнера (и уничтожая их).
Если n больше текущего размера контейнера, содержимое расширяется, вставляя в конце столько элементов, сколько необходимо, чтобы достичь размера n. Если указан val, новые элементы инициализируются как копии val, в противном случае они инициализируются значением.
Если n также больше, чем текущая емкость контейнера, происходит автоматическое перераспределение выделенного пространства хранения .
Обратите внимание, что эта функция изменяет фактическое содержимое контейнера, вставляя или удаляя из него элементы.
Я думаю, это не совсем описывает, «как» она выполняет перераспределение, но, конечно же, копия в памяти - это самый быстрый способ переместить вектор в его недавно выделенное пространство памяти?
Так почему же вызываются торы копирования / перемещения c, когда std :: vector добавляется вместо копия из памяти?
Примечание / вопрос: (возможно, это должен быть отдельный вопрос): Лично переместите c, чтобы узнать, почему напечатано moved fred -> fred.moved
, а не moved -> fred.moved
. Похоже, что назначение перемещения std :: string на самом деле не "перемещает" данные ...