Pass by reference to a function accepting a universal reference

193 Views Asked by At

I am trying to use an API which sets a value of a variable based on an HTTP call. The function in which I can set the variable which will be set upon an HTTP Call is of type T&&. I would like to access this variable on a different thread. I tried to simplify the problem and represent it in the following code, as two threads, accessing a variable the same way.

#include <iostream>
#include <thread>
#include <chrono>

class Values
{
public:
    int i;
    std::string s;
};


template<typename T>
void WriteCycle(T&& i)
{
    using namespace std::chrono_literals;
    while (true)
    {
        i++;
        std::cout << i << std::endl;
        std::this_thread::sleep_for(500ms);
    }

}
template<typename T>
void ReadCycle(T&& i)
{
    using namespace std::chrono_literals;

    while (true)
    {
        std::cout << i << std::endl;
        std::this_thread::sleep_for(500ms);
    }

}

int main() {


    auto v = new Values();

    std::thread t1(WriteCycle<int>, v->i);
    std::thread t2(ReadCycle<int>, v->i);


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

Currently the read thread does not see any change in the variable. I read up an perfect forwarding, move semnatics and forwardin references, but I did not get it (I mostly program dotnet, my c++ knowledge is pre C++11). I tried all combinations of std::move, std::ref and std::forward but I cannot get the read thread to see the change of the write thread. Is there a way to solve this, without changing the T&& input type of the functions (since that is part of the API I am trying to use)? How to solve this in a thread-safe way?

1

There are 1 best solutions below

3
On BEST ANSWER

Such notation:

template<typename T>
void WriteCycle(T&& i)

Doesn't really mean an rvalue reference, it means a universal reference, which could be an lvalue reference or rvalue reference depending on what kind of data you pass.

In your case it turns into just an lvalue reference, so it has nothing to do with move semantic. The problem is that thread constructor is not quite friendly with references and you may want just to use std::ref to get it round:

auto myRef = std::ref(v->i);
std::thread t1(WriteCycle<&int>, myRef);
std::thread t2(ReadCycle<&int>, myRef);

However it still won't be perfect, because you want to synchronize between threads with use of mutexes or atomic values