I need to store a sequence of elements of type ThirdPartyElm
, and I'm using a std::vector
(or a std::array
if I need a fixed size sequence).
I'm wondering how I should initialise the sequence. The first version creates a new element and (if I'm right) creates a copy of the element when it is inserted in the sequence:
for (int i = 0; i < N; i++)
{
auto elm = ThirdPartyElm();
// init elm..
my_vector.push_back(elm); // my_array[i] = elm;
}
The second version stores a sequence of pointers (or better smart pointers with c++11):
for (int i = 0; i < N; i++)
{
std::unique_ptr<ThirdPartyElm> elm(new ThirdPartyElm());
// init elm..
my_vector.push_back(std::move(elm)); // my_array[i] = std::move(elm);
}
Which is the most lightweight version?
Please highlight any errors.
You can just declare it with the size, and it will call the default constructor on those elements.
std::vector<ThirdPartyElem> my_vector(N);
As far as your statement
The first version creates a new element and (if I'm right) creates a copy of the element when it is inserted in the sequence
Don't worry about that. Since ele
is a local variable that is about to fall out of scope, your compiler will likely use copy elision such that a move
will be invoked instead of a copy
.
I was mistaken about the above, please disregard that.
Avoid dynamic allocation whenever you can. Thus, generally prefer saving the elements themselves instead of smart-pointers to them in the vector.
That said, either is fine, and if ThirdPartyElem
is polymorphic, you wouldn't have a choice.
Other considerations are the cost and possibility of moving and copying the type, though generally don't worry.
There are two refinements to option one which might be worthwhile though:
std::move
the new element to its place, as that is probably less expensive than copying (which might not even be possible).
If the type is only copyable and not movable (legacy, ask for update), that falls back to copying.
Try to construct it in-place, to eliminate copy or move, and needless destruction.
for (int i = 0; i < N; i++)
{
my_vector.emplace_back();
try {
auto&& elm = my_vector.back();
// init elm..
} catch(...) {
my_vector.pop_back();
throw;
}
}
If the initialization cannot throw, the compiler will remove the exception-handling (or you can just omit it).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With