I have a vector of structures that are copy-constructible, but non-assignable:
struct Struct
{
inline Struct(const std::string& text, int n) : _text(text), _n(n) {}
inline Struct(const Struct& other) : _text(other._text), _n(other._n) {}
const std::string _text;
const int _n;
Struct& operator=(const Struct&) = delete;
};
It all worked fine. In fact, I could even pass the std::vector<Struct>
around by value as a return value of a function. And yet, this fails:
std::vector<TextFragment> v1, v2;
v2 = v1;
The error, of course, is:
error: C2280: 'Struct &Struct ::operator =(const Struct &)' : attempting to reference a deleted function
I don't understand why it tries to invoke that. Is that some kind of an optimization to avoid re-allocating the vector's memory block?..
The default copy constructor will copy all members – i.e. call their respective copy constructors. So yes, a std::vector (being nothing special as far as C++ is concerned) will be duly copied.
At the time of declaration of vector, passing an old initialized vector copies the elements of the passed vector into the newly declared vector. They are deeply copied.
vector:: assign() is an STL in C++ which assigns new values to the vector elements by replacing old ones. It can also modify the size of the vector if necessary.
Is that some kind of an optimization to avoid re-allocating the vector's memory block?..
Almost. It is an optimization to avoid re-allocating whatever memory blocks might be present in vector
's value_type
. That is, it is a global assumption that assignment can be more efficient than destruction followed by copy construction.
For example consider vector<string>
assignment, for two equal sized vector
s, and a bunch of equal sized string
s in each spot in the vector
:
v2 = v1;
All this operation has to do is memcpy
each string
. No allocations at all. Reducing allocations/deallocations is one of the most important optimizations that exist today.
However, all is not lost for your Struct
. What you want to do is instruct the vector
that you do not wish to assign your Struct
s, but instead destruct them, and then copy construct them from v1
. The syntax for doing this is:
v2.clear();
for (const auto& x : v1)
v2.push_back(x);
As noted in the comments below, you can also copy construct v1
, and then swap
with the copy. You either need to create a temporary local copy of v1
, or you need to use v1
on the "lhs" of member swap:
std::vector<Struct>(v1).swap(v2);
I personally find this hard to read. In C++11/14 I prefer this alternative which involves move assignment:
v2 = std::vector<Struct>(v1);
These alternatives are easier on the eyes. But the first alternative, using clear()
and push_back
is going to be the most efficient on average. This is because the first alternative is the only one that has a chance of reusing capacity()
in v2
. The other two always recreate new capacity()
in the copy of v1
and throw away v2
's existing capacity()
.
A std::vector
is a allocator-aware container. If we look at the spec for that(table 99) we have a = t
where a
is a non-const lvalue and t
is a lvalue or const rvalue and it requires
T is CopyInsertable into X and CopyAssignable. post: a == t
Emphasis mine
Where T
is the value_type
of X
(the container). It also states that the operation is linear. Since T
needs to be CopyAssignable and Struct
is not CopyAssignable it is not required to work.
This implies that the assignment operation would look something like:
std::vector<T>& operator=(const std::vector<T>& rhs)
{
// allocate enough room for data if needed
for (std::size_t i = 0; i < rhs.size(); ++i)
data[i] = rhs.data[i];
return *this;
}
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