Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store objects without copy or move constructor in std::vector?

To improve efficiency of std::vector<T>, it's underlying array needs to be pre-allocated and sometimes re-allocated. That, however, requires the creation and later moving of objects of type T with a copy ctor or move ctor.

The problem that I am having is that T cannot be copied or moved because it contains objects that cannot be copied or moved (such as atomic and mutex). (And, yes, I am implementing a simple thread pool.)

I would like to avoid using pointers because:

  1. I do not need a level of indirection and so I do not want one.
  2. (Pointers are less efficient and increase complexity. Using pointers increases memory fragmentation and decreases data locality which can (but not necessarily must) cause a noticeable performance impact. Not so important, but still worth consideration.)

Is there a way to avoid a level of indirection here?

UPDATE: I fixed some incorrect assumptions and re-phrased the question, based on feedback in comments and answers.

like image 383
Domi Avatar asked Dec 10 '13 06:12

Domi


People also ask

Is std :: move required?

std::move itself does "nothing" - it has zero side effects. It just signals to the compiler that the programmer doesn't care what happens to that object any more. i.e. it gives permission to other parts of the software to move from the object, but it doesn't require that it be moved.

Can vectors store objects C++?

Storing and Accessing Class Objects in a Vector The primary difference in using vectors for storing class objects versus using an array, other than all the differences between vectors and arrays, is how you declare the vector to store class objects. We can use the same Student class definition as shown earlier.

Does vector have a move constructor?

No. It doesn't call the move constructor. To call move constructor of element you will have to call std::move while pushing to vector itself.


2 Answers

For the start, std::mutex can not be copied or moved, therefore you are forced to use some kind of indirection.

Since you want to store mutex in a vector, and not copy it, I would use std::unique_ptr.

vector<unique_ptr<T>> does not allow certain vector operations (such as for_each)

I am not sure I understand that sentence. It is perfectly possible to do range for :

std::vector< std::unique_ptr< int > > v; // fill in the vector for ( auto & it : v )   std::cout << *it << std::endl; 

or to use std algorithms :

#include <iostream> #include <typeinfo> #include <vector> #include <memory> #include <algorithm>   int main() {     std::vector< std::unique_ptr< int > > v;     v.emplace_back( new int( 3 ) );     v.emplace_back( new int( 5 ) );     std::for_each( v.begin(), v.end(), []( const std::unique_ptr< int > & it ){ std::cout << *it << std::endl; } ); } 
like image 166
BЈовић Avatar answered Sep 23 '22 11:09

BЈовић


That requires however the creation of objects of type T with a copy ctor.

That is not entirely right, as of C++11, if you use the constructor of std::vector which will default construct a number of elements, then you don't need to have a copy or move constructor.

As such, if no threads are added or deleted from your pool, you can then just do:

int num = 23; std::vector<std::mutex> vec(num); 

If you want to add or delete things dynamically, then you have to use an indirection.

  1. Use std::vector + std::unique_ptr as already proposed
  2. Use a std::deque, that allows you to neatly use it with range based for loops or std-algorithms and avoids all indirections. (Which only allows additions)
  3. Use a std::list/forward_list this solution is similar to number one, however it has the additional benefit of easier usage with range based for and algorithms. It's probably the best if you are only accessing the elements sequentially as there is no support for random-access.

Like this:

std::deque<std::mutex> deq; deq.emplace_back(); deq.emplace_back();  for(auto& m : deq) {     m.lock(); } 

As a final note, std::thread is of course moveable, so you can use std::vector + std::vector::emplace_back with it.

like image 42
Stephan Dollberg Avatar answered Sep 23 '22 11:09

Stephan Dollberg