问题
Below code passes objects that contain big vectors into a vector. I want this to be performant. Do I need to cast test
to rvalue in the call to push_back
? Do I need to tell compiler how to move instances of struct Test
? Or does this all go automatically?
int main()
{
struct Test
{
std::vector<size_t> vals;
double sum;
};
std::vector<Test> vecOfTest;
vecOfTest.reserve(100000);
for (size_t i = 0; i < 100000; i++)
{
Test test{};
test.vals.reserve(i);
for (size_t j = 0; j < i; j++)
{
test.vals.push_back(j);
test.sum += j;
}
vecOfTest.push_back(test);
}
return 0;
}
回答1:
Your Test
struct does not define any special member functions (copy constructor, destructor, etc.) That means a default move assignment operator and a default move copy constructor are generated automatically, and they will move each data member of the struct. So Test
is a movable type, and it benefits from that since vector<size_t>
is a movable data member.
However, moves are not performed automatically because moving from an object changes it. Even though you'd think that this:
vecOfTest.push_back(test);
}
would do an implicit move because the scope ends, it will not. Implicit moves would put both the compiler and the programmer in a difficult situation. The compiler would be required to prove that invalidating test
is OK. The programmer would be required to constantly investigate whether or not an explicit move is needed or not, and the end result of that would be to just do explicit moves anyway. So for that reason, implicit moves do not happen (but see below for the exception to the rule.) You need to do it yourself:
vecOfTest.push_back(std::move(test));
The only case where you need to not move is when the move would interfere with elision. For example, in a function that returns a Test
, this:
Test test;
return std::move(test);
would move, but it's better not to. It's better to:
return test;
instead. This is not an implicit move. It's an elision. Elision is faster than move, and doing a move would prevent elision. However, in cases where elision is not possible, then an implicit move is performed. This is the only case I know of where an implicit move will happen: as a substitute for elision. Your original code:
vecOfTest.push_back(test);
is not a case for elision, and so an implicit move will never happen.
回答2:
I want this to be performant
Following should be good enough. I hope the comments will help you to understand the code.
#include <vector>
#include <iostream>
#include <numeric>
struct Test
{
std::vector<size_t> vals;
double sum = 0; // initialing is a good idea
Test(const size_t v, const double res) // provide constructor(appropriate one)
: vals(v), // tell the size of the vals directly in the constructor
sum(res)
{}
};
int main()
{
std::vector<Test> vecOfTest;
vecOfTest.reserve(100000);
for (size_t i = 0; i < 100000; i++)
{
// Test& last_test = vecOfTest.emplace_back() needs C++17, otherwise
// use std::vector::back()
auto& last_test = vecOfTest.emplace_back( // create the Test object in place and take the reference to it
i, // tell the size of vals in newly creating Test object
((i - 1) * i) / 2.0 // ((j-1) * j)/2 = sum from 0 to j-1
);
std::iota(std::begin(last_test.vals), std::end(last_test.vals), static_cast<size_t>(0)); // populate, 0 to size of vals
}
return 0;
}
来源:https://stackoverflow.com/questions/55965412/how-to-enable-move-semantics-when-adding-custom-objects-to-a-vector