Reputation: 105
I have a question about a class that we can store in vector. What is the requirement that can be stored in a vector? It seems that such class has to have assignment operator. But I am not sure if that's all or not.
Let me give you an example. class A has const int member. If I don't write operator =, it doesn't compile. But in this example, this operator does nothing. This program displays 10 and 20 correctly. It looks that operator = is required but not used in reality.
#include <iostream>
#include <vector>
class A {
public:
A(int a) : a_(a) {}
A& operator =(const A& a2) { return *this;} // Without this, compile fails.
void print() const {
std::cerr << a_ << std::endl;
}
private:
const int a_;
};
int main(int argc, char** argv) {
std::vector<A> v;
v.push_back(A(10));
v.push_back(A(20));
for (const A& a : v) a.print();
}
Upvotes: 8
Views: 3840
Reputation: 24946
If I don't write operator =, it doesn't compile.
That surprised me, so I had a look into the standard and I found:
The only expression that puts constraints on the type used by the vector
in your example is push_back
.
The push_back()
method of a sequence container type X<T,A>
with allocator A
and value_type
T
, requires T
to be:
Which means it requires a valid copy constructor or (as in this case) a valid move constructor which will be implicitly present from your code. Therefore the compilation should not fail in any compiler with a valid C++11 standard library.
vector
to be assignable:typdef std::vector<T> X;
X a,b;
X&& rv;
X::value_type t;
X::value_type&& u;
X::size_type n;
X::const_iterator p,q; // p = valid for a, q = valid and dereferencable
initializer_list<T> il;
[i,j) -> valid iterator-range
*
list of operationsThe operations, which require T
to be assignable, if X is a vector
, are:
Statement Requirement on T
a = b; CopyInsertable, CopyAssignable
a = rv; MoveInsertable, MoveAssignable
a = il; CopyAssignable
a.emplace(p, args); MoveInsertable, MoveAssignable
a.insert(p, t); CopyAssignable
a.insert(p, u); MoveAssignable
a.insert(p, n, t); CopyInsertable, CopyAssignable
a.insert(p, i, j); EmplaceConstructible[from *i], MoveInsertable, MoveAssignable
a.insert(p, il); -> a.insert(p, il.begin(), il.end());
a.erase(q); MoveAssignable
a.erase(q1,q2) MoveAssignable
a.assign(i,j); Assignable from *i
a.assign(il); -> a.assign(il.begin(), il.end());
a.assign(n,t) CopyAssignable
*
= Pessimistic means that there may exist certain conditions for several requirements to actually come into effect. If you use one of the expressions listed above, your type T
will likely be required to be assignable.
Upvotes: 3
Reputation: 185902
This might surprise you:
v.push_back(A(20));
v.push_back(A(10));
std::sort(begin(v), end(v));
There are aspects of vector itself that require assignability, though I don't know, offhand, which (and I can't tell by compiling your code, since my compiler doesn't complain when I remove operator=()
). According to Wikipedia (which references the relevant portion of the '03 standard), elements must be CopyConstructible
and Assignable
.
EDIT: Coming back to this a day later, it seems forehead-slappingly obvious when std::vector
requires Assignable
— any time it has to move elements around. Add a call to v.insert()
or v.erase()
, for example, and the compile will fail.
Upvotes: 6
Reputation: 786
push_back on vector will make vector grow in memory, that means old objects needs to be copied to new object via assignment operator= hence you need assignment operator=.
Upvotes: 2
Reputation: 1136
The availability of copy constructor and assignment operator are guaranteed by the compiler if you don't create any of your own. (the correctness of default implementation, is a different story though). In your example, you almost don't have to overload A::opeartor=().
The problem here is not a "missing" operator=(), but that the default one cannot be used, because you have declared a const data member
const int a_;
The default compiler generated operator=() cannot assign value to a const member. So you have to overload your self:
A & A::opeartor=(const A & in)
{
*const_cast<int*>(&a_) = in.a_; // !!!you are expected to do this.
}
So, your version of A::operator=() that does nothing, although making the code compile, does't change the a_ value of the left hand operand,
Upvotes: -1