Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why std::vector requires operator =

Tags:

c++

stl

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();
}
like image 507
zorio Avatar asked Jul 20 '13 09:07

zorio


People also ask

Does vector have operator C++?

vector::operator= This operator is used to assign new contents to the container by replacing the existing contents. It also modifies the size according to the new contents.

What does std::vector do?

1) std::vector is a sequence container that encapsulates dynamic size arrays. 2) std::pmr::vector is an alias template that uses a polymorphic allocator. The elements are stored contiguously, which means that elements can be accessed not only through iterators, but also using offsets to regular pointers to elements.

Is std::vector slower than array?

A std::vector can never be faster than an array, as it has (a pointer to the first element of) an array as one of its data members. But the difference in run-time speed is slim and absent in any non-trivial program. One reason for this myth to persist, are examples that compare raw arrays with mis-used std::vectors.


2 Answers

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.

like image 143
Marcelo Cantos Avatar answered Oct 06 '22 20:10

Marcelo Cantos


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.

like image 29
Pixelchemist Avatar answered Oct 06 '22 19:10

Pixelchemist