Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Double envoking of copy-constructor of a class passed into std::vector::emplace_back [duplicate]

Tags:

c++

c++11

I'd like to understand the cause of double envoking of copying-constructor in the following code:

#include <vector>
#include <thread>
#include <algorithm>
#include <functional>
#include <iostream>
#include <mutex>

std::mutex m_stdout;

class MyWorker {
public:
  MyWorker() = default;
  MyWorker(const MyWorker& mw) {
    m_stdout.lock();
    std::cout << "MyWorker coping" << std::endl;
    m_stdout.unlock();
  }

  void operator() () {}
};

int main () {
  std::vector<std::thread> vth;
  vth.reserve(4);
  MyWorker mw;
  for (int i = 4; i > 0; --i) {
    vth.emplace_back(mw);
  }
  std::for_each(vth.begin(), vth.end(), std::mem_fn(&std::thread::join));
}

stdout:

MyWorker coping
MyWorker coping
MyWorker coping
MyWorker coping
MyWorker coping
MyWorker coping
MyWorker coping
MyWorker coping

emplace_back should not make two copies, should it?

like image 671
dronte7 Avatar asked Dec 02 '19 14:12

dronte7


1 Answers

This is really subtle: you need a move constructor. Basically, it DOES copy in once into the vector when you're making the thread, but then when the threads LAUNCH, they need to either copy or move their arguments in. You only have a copy constructor, so that's what it uses. I added this code below to demonstrate:

MyWorker(MyWorker&& mw) {
  m_stdout.lock();
  std::cout << "MyWorker moving" << std::endl;
  m_stdout.unlock();
}

Add that in addition to the copy constructor you already have and you'll get this output:

MyWorker copying
MyWorker moving
MyWorker copying
MyWorker moving
MyWorker copying
MyWorker moving
MyWorker copying
MyWorker moving
like image 112
Kevin Anderson Avatar answered Nov 14 '22 23:11

Kevin Anderson