Должен ли я изменить этот код, который использует push_back для использования std::move?
Я только что прочитал о ссылках rvalue, и ниже приведен код, на который я ссылаюсь
vector<string> v;
string s = "hello";
v.push_back(s);
cout << s << endl;
cout << v[0] << endl;
return 0
Мой вопрос заключается в том, что до сих пор, всякий раз, когда я видел вектор объектов (или строк), я в основном вижу вставки, как описано выше. Вместо этого, если бы я сделал v.push_back(std::move(s))
тогда я не буду создавать ненужные дубликаты объектов.
Должен ли это быть правильный способ добавления объектов в STL, где все, что нам нужно, это данные в контейнере, а не переменные.
2 ответа
Учитывая комментарии, которые я получил, я выбрал другой подход к этому ответу. Вот программа, которая использует различные способы добавления объекта в вектор. Эта программа выводит, какие именно конструкторы копирования, конструкторы перемещения, операторы копирования, операторы перемещения и деструкторы вызываются во всех случаях.
Программа использует чудесную библиотеку fmt. А также fmt::print
делает именно то, что вы думаете, что делает. Я должен, возможно, использовать ::fmt
, Имейте в виду, что перемещение от объекта вообще оставляет его в неопределенном (заметьте, не совпадает с неопределенным) состоянии. Конечно, для классов, которые вы пишете сами, вы точно знаете, в каком состоянии они находятся. Но для стандартных библиотечных классов или классов, написанных другими людьми, вы этого не делаете. По-видимому, для стандартных библиотечных классов вам гарантировано, что если вы сделаете что-то, чтобы установить состояние на что-то известное, то объект действительно изменится на это состояние.
Лично я бы относился к объекту так, как будто единственная допустимая операция над ним после того, как вы от него ушли, - это вызвать его деструктор. Но я вижу случаи, когда вы можете просто захотеть повторно использовать хранилище каким-то образом.
Вот программа:
#include <fmt/core.h> // fmt
#include <vector> // vector
#include <utility> // move
class TestClass {
public:
TestClass(int a, int b) {
fmt::print("TestClass{{{}, {}}}\n", a, b);
}
TestClass(TestClass const &) noexcept {
fmt::print("TestClass{{TestClass const &}}\n");
}
TestClass(TestClass &&) noexcept {
fmt::print("TestClass{{TestClass &&}}\n");
}
TestClass const &operator =(TestClass const &) noexcept {
fmt::print("=(TestClass const &)\n");
return *this;
}
TestClass const &operator =(TestClass &&) noexcept {
fmt::print("=(TestClass &&)\n");
return *this;
}
~TestClass() noexcept {
fmt::print("~TestClass()\n");
}
};
int main()
{
::std::vector<TestClass> v;
// Reserve necessary space so movements of vector elements doesn't clutter up
// the output.
v.reserve(6);
fmt::print("Constructing initial\n");
TestClass o{1, 2};
fmt::print("\bv.push_back(o)\n");
v.push_back(o);
fmt::print("\nv.push_back(::std::move(o))\n");
v.push_back(::std::move(o));
fmt::print("\nv.push_back(TestClass{{3, 4}})\n");
v.push_back(TestClass{3, 4});
fmt::print("\nv.emplace_back(5, 6)\n");
v.emplace_back(5, 6);
fmt::print("\nv.emplace_back(::std::move(o))\n");
v.emplace_back(::std::move(o));
fmt::print("\nv.emplace_back(TestClass{{5, 6}})\n");
v.emplace_back(TestClass{5, 6});
fmt::print("\nHere H\n");
}
Вот вывод программы:
Constructing initial
TestClass{1, 2}
v.push_back(o)
TestClass{TestClass const &}
v.push_back(::std::move(o))
TestClass{TestClass &&}
v.push_back(TestClass{3, 4})
TestClass{3, 4}
TestClass{TestClass &&}
~TestClass()
v.emplace_back(5, 6)
TestClass{5, 6}
v.emplace_back(::std::move(o))
TestClass{TestClass &&}
v.emplace_back(TestClass{5, 6})
TestClass{5, 6}
TestClass{TestClass &&}
~TestClass()
Here H
~TestClass()
~TestClass()
~TestClass()
~TestClass()
~TestClass()
~TestClass()
~TestClass()
Я скажу, что эта программа имеет именно тот результат, который я ожидал, и этот результат (AFAIK) соответствует ответу, который я получил здесь ранее.
Почему я использую ::std::
вместо std::
Q: что следует использовать?
// emplace_back создает элементы на месте.
emplace_back("element");
// push_back создаст новый объект и затем скопирует (или переместит) его значение аргументов.
push_back(explicitDataType{"element"});
Так что здесь вы должны использовать
emplace_back()
вместоstd::move()
,