c++multithreadingfunctionstdthread

c++ thread function accepting object by value: why does std::ref(obj) compile?


#include <iostream>
#include <thread>

template<int Num>
class MyClass {
public:
    MyClass(int val) : val_(val) {}

    // Copy constructor
    MyClass(const MyClass& other) : val_(other.val_) {
        std::cout << "Copy constructor called" << std::endl;
    }

    // Move constructor
    MyClass(MyClass&& other) noexcept : val_(other.val_) {
        std::cout << "Move constructor called" << std::endl;
    }

private:
    int val_;
};

template<int Num>
void threadFunction(MyClass<Num> myObj) {
    std::cout << "Inside thread" << std::endl;
}

int main() {
    MyClass<1> obj(42);

    std::thread t1(threadFunction<1>, obj); // <-- pass by value
    std::thread t2(threadFunction<1>, std::ref(obj)); // <-- pass by reference

    t1.join();
    t2.join();

    return 0;
}

I want to pass an object by value to a thread to ensure each thread works on its own copy (thread t1).

When I create thread t2, I pass the object by reference using std::ref(obj).

Clearly, my intention per function definition is to pass the object by value rather than by reference. Is obj a shared variable if passed as std::ref(obj) among all threads?

This question std::ref using with template function and function passing by value seems to address a similar topic, but not in multithreading context. In the example shown there, there seems to be a difference if the function is templated.

EDIT 1:

I added a copy and move constructor to the class motivated by the discussion in the answer below.


Solution

  • Why would it not compile?

    std::ref returns a std::reference_wrapper which has an implicit conversion to reference to stored object that is used here.

    Is obj a shared variable if passed as std::ref(obj) among all threads?

    No. Each thread gets a copy.

    The situation is similar to

     void foo( int a) { std::cout << a; }
    
     int main() {
         int x = 42;
         int& ref = x;
         foo(ref);
     }
    

    ref is a reference to x. When you call foo(ref) then a is a copy of x.

    Actually the need to use std::ref arises because the thread constructor always gets its parameters by value. And if the function called by the thread is template <typename Num> void threadFunction(MyClass<Num> myObj), ie it accepts a value, then nothing can magically change it to get myObj passed by reference.