我看到了一个关于 c++11 并发性的 Youtube 视频(第 3 部分)和以下代码,它在视频中编译并生成了正确的结果。
但是,我在使用 Visual Studio 2012 时遇到此代码的编译错误。编译器提示 toSin(list<double>&&)
的参数类型.如果我将参数类型更改为 list<double>&
, 编译的代码。
我的问题是 move(list)
返回了什么在_tmain()
, 它是右值引用还是只是一个引用?
#include "stdafx.h"
#include <iostream>
#include <thread>
#include <chrono>
#include <list>
#include <algorithm>
using namespace std;
void toSin(list<double>&& list)
{
//this_thread::sleep_for(chrono::seconds(1));
for_each(list.begin(), list.end(), [](double & x)
{
x = sin(x);
});
for_each(list.begin(), list.end(), [](double & x)
{
int count = static_cast<int>(10*x+10.5);
for (int i=0; i<count; ++i)
{
cout.put('*');
}
cout << endl;
});
}
int _tmain(int argc, _TCHAR* argv[])
{
list<double> list;
const double pi = 3.1415926;
const double epsilon = 0.00000001;
for (double x = 0.0; x<2*pi+epsilon; x+=pi/16)
{
list.push_back(x);
}
thread th(&toSin, /*std::ref(list)*/std::move(list));
th.join();
return 0;
}
最佳答案
这似乎是 MSVC2012 中的错误。 (以及快速检查,MSVC2013 和 MSVC2015)
thread
不直接使用完美转发,因为在原始线程中存储对数据(临时或非临时)的引用并在派生线程中使用它会极易出错且危险。
相反,它将每个参数复制到 decay_t<?>
中的内部数据。
错误在于,当它调用 worker 函数时,它只是将该内部拷贝传递给您的过程。相反,它应该将内部数据 move 到调用中。
这似乎没有在编译器版本 19 中修复,我认为它是 MSVC2015(没有仔细检查),基于在这里编译您的代码
这既是由于标准的措辞(它应该调用 decay_t<F>
和 decay_t<Ts>...
—— 这意味着右值绑定(bind),而不是左值绑定(bind)),也是因为存储在线程中的本地数据永远不会在调用您的过程后再次使用(因此从逻辑上讲,它应该被视为过期数据,而不是持久数据)。
这里有一个解决方法:
template<class F>
struct thread_rvalue_fix_wrapper {
F f;
template<class...Args>
auto operator()(Args&...args)
-> typename std::result_of<F(Args...)>::type
{
return std::move(f)( std::move(args)... );
}
};
template<class F>
thread_rvalue_fix_wrapper< typename std::decay<F>::type >
thread_rvalue_fix( F&& f ) { return {std::forward<F>(f)}; }
然后
thread th(thread_rvalue_fix(&toSin), /*std::ref(list)*/std::move(list));
应该可以。 (在上面链接的 MSVC2015 在线编译器中测试过)根据个人经验,它应该也适用于 MSVC2013。我不知道 MSVC2012。
关于c++ - "move semantics"和 "rvalue reference"的 Visual Studio 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28330471/