0

I'd like to verify my understanding of how std::thread makes a copy of a shared_ptr when it is passed one as an argument.

Assume the following code (C++ 17):

#include <iostream>
#include <memory>
#include <thread>


void const_reference(const std::shared_ptr<int>& data_ptr)
{
  std::cout << "value: " << *data_ptr << std::endl;
  std::cout << "shared_ptr address: " << &data_ptr << std::endl;
  std::cout << "data address: " << data_ptr.get() << std::endl;
  std::cout << "Use count: " << data_ptr.use_count() << std::endl;
}


int main()
{
  std::shared_ptr<int> data_ptr = std::make_shared<int>(42);

  const_reference(data_ptr); // Use count stays 1 as expected - no copy is made.
  /*
   * value: 42
   * shared_ptr address: 0x7ff7ba1b9418
   * data address: 0x6000031a91b8
   * Use count: 1 
   */

  std::thread worker {const_reference, data_ptr}; // Use count is 2 - a copy is made.
  /*
   * value: 42
   * shared_ptr address: 0x6000031a91d0
   * data address: 0x6000031a91b8
   * Use count: 2
   */

  worker.join();

  return 0;
}

According to cppreference the constructor is as follows:

template< class Function, class... Args > 
explicit thread( Function&& f, Args&&... args );
// The new thread of execution starts executing

INVOKE(decay-copy(std::forward<Function>(f)),
       decay-copy(std::forward<Args>(args))...).

If I understand correctly, a copy of the args is already made (using decay-copy) before it is passed to f, so the x in const_reference(x) is a copy of the shared_ptr (and of course that can be then taken as a const&). Is this correct? I'd very much appreciate it if someone with a deep understanding could write clear explanation.

A side note: clang-tidy recommends taking a const reference to a shared_ptr if it is only used as a const reference, but I'd argue that it is clearer to make it explicit a copy is made when it is used in this way.

0 Answers0