zorio
zorio

Reputation: 105

Why std::vector requires operator =

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

Answers (4)

Pixelchemist
Pixelchemist

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:

Your example has an implicitly deleted copy constructor but should still compile if a conforming C++11 standard library is at hand.

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:

  • CopyInsertable if an lvalue or const rvalue reference is passed
  • MoveInsertable if a non-const rvalue is passed

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.

Operations that require the type, contained in a vector to be assignable:

Ancillary conditions

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

Pessimistic* list of operations

The 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

Marcelo Cantos
Marcelo Cantos

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

Anand Rathi
Anand Rathi

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

ROTOGG
ROTOGG

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

Related Questions