Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::thread pass by reference calls copy constructor

Well I have an issue with passing data into a thread using std::thread. I thought I understood the general semantics of copy constructors, etc. but it seems I don't quite grasp the problem. I have a simple class called Log that has hidden it's copy constructor thusly:

class Log { public:     Log(const char filename[], const bool outputToConsole = false);     virtual ~Log(void);      //modify behavior     void appendStream(std::ostream *);     //commit a new message     void commitStatus(const std::string str);  private:     //members     std::ofstream fileStream;     std::list<std::ostream *> listOfStreams;      //disable copy constructor and assignment operator     Log(const Log &);     Log & operator=(const Log &); } 

now I have a main based heavily on http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/example/cpp11/echo/blocking_tcp_echo_server.cpp

int main() {     static int portNumber = 10000;      Log logger("ServerLog.txt", true);     logger.commitStatus("Log Test String");      try {         boost::asio::io_service ioService;         server(ioService, portNumber, logger);     }     catch (std::exception &e)     {         std::cerr << "Exception " << e.what() << std::endl;         logger.commitStatus(e.what());     }      return 0; } 

You can see that main calls the function server and passes the IOService, portNumber and logger. The logger is passed by reference, thusly:

using boost::asio::ip::tcp;  void server(boost::asio::io_service &ioService, unsigned int port, Log &logger) {     logger.commitStatus("Server Start");      tcp::acceptor acc(ioService, tcp::endpoint(tcp::v4(), port));      while(true)     {         tcp::socket sock(ioService);         acc.accept(sock);          std::thread newThread(session, &sock, logger);         newThread.detach();     }      logger.commitStatus("Server closed"); } 

I get a compiler error when I try to pass the logger (or the socket) to the thread by reference, but I do not get the error when passing it to the session() by reference

static void session(tcp::socket *sock, Log &logger) {     std::cout << " session () " << std::endl; } 

Now I thought that I understood correctly that a reference is the same as passing a pointer. That is, it does not call the copy constructor, it simply passes the pointer, which it lets you syntactically treat like it's not a pointer.

error C2248: 'Log::Log' : cannot access private member declared in class 'Log'

1> \log.h(55) : see declaration of 'Log::Log'

1> \log.h(28) : see declaration of 'Log'

...

: see reference to function template instantiation 'std::thread::thread(_Fn,_V0_t &&,_V1_t)' being compiled

1> with

1> [

1> Fn=void (_cdecl *)(boost::asio::ip::tcp::socket *,Log &),

1> _V0_t=boost::asio::ip::tcp::socket *,

1> _V1_t=Log &

1> ]

However if I modify it to pass a pointer, everything is happy

...         std::thread newThread(session, &sock, &logger); ...  static void session(tcp::socket *sock, Log *logger) {     std::cout << " session () " << std::endl; } 

Why is passing by reference calling my copy constructor. Is there something special happening here because of std::thread? Did I misunderstand the copy constructor and pass by reference?

I get a different but equally baffling error if I try to use std::move() as it is done in the example. Is it possible my VS2012 is not implementing C++11 correctly?

like image 446
xaviersjs Avatar asked Jan 10 '14 16:01

xaviersjs


People also ask

Does passing by reference call copy constructor?

The point of "pass by reference" is to not make a copy as soon as Employee constructor is called, but only when you choose to initialize one of Employee's member with the Date passed.

Does pass by value use copy constructor?

Passing by value means that the parameter is copied into the function. That calls the copy constructor.

Is std :: thread copyable?

std::thread::operator= thread objects cannot be copied (2).


2 Answers

std::thread takes its arguments by value. You can get reference semantics back by using std::reference_wrapper:

std::thread newThread(session, &sock, std::ref(logger)); 

Obviously you must make sure that logger outlives the thread.

like image 106
juanchopanza Avatar answered Oct 07 '22 15:10

juanchopanza


I get a compiler error when I try to pass the logger (or the socket) to the thread by reference

It is not sufficient for the thread's entrypoint function to take a reference type: the thread object itself takes its arguments by value. This is because you usually want a copy of objects in a separate thread.

To get around this, you may pass std::ref(logger), which is a reference wrapper hiding reference semantics under a copyable object.

like image 39
Lightness Races in Orbit Avatar answered Oct 07 '22 16:10

Lightness Races in Orbit