通过引用传递给接受通用引用的函数

Pass by reference to a function accepting a universal reference

我正在尝试使用 API 来设置基于 HTTP 调用的变量值。我可以在其中设置将在 HTTP 调用时设置的变量的函数是 T&& 类型。我想在不同的线程上访问这个变量。我试图简化问题并在以下代码中将其表示为两个线程,以相同的方式访问变量。

#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();
}

目前读线程没有看到变量有任何变化。我阅读了完美的转发、移动语义学和转发引用,但我没有理解(我主要编写 dotnet,我的 C++ 知识是 C++11 之前的知识)。我尝试了 std::move、std::ref 和 std::forward 的所有组合,但我无法让读取线程看到写入线程的变化。有没有办法在不更改函数的 T&& 输入类型的情况下解决这个问题(因为这是我尝试使用的 API 的一部分)?如何以线程安全的方式解决这个问题?

这样的表示法:

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

并不是真正意义上的右值引用,它是指通用引用,它可以是左值引用或右值引用,具体取决于您传递的数据类型。

在你的例子中,它只是一个左值引用,所以它与移动语义无关。问题是线程构造函数对引用不太友好,你可能只想使用 std::ref 来解决它:

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

然而它仍然不是完美的,因为你想在线程之间使用互斥锁或原子值进行同步