C++ 在std::bind和std::thread中移动语义/行为
请看下面的简单测试程序,您可以直接复制和测试。我试过使用GCC4.9,它编译得很好C++ 在std::bind和std::thread中移动语义/行为,c++,c++11,gcc,move-semantics,stdthread,C++,C++11,Gcc,Move Semantics,Stdthread,请看下面的简单测试程序,您可以直接复制和测试。我试过使用GCC4.9,它编译得很好 #include <iostream> #include <functional> #include <thread> #include <string> class Test { public: Test(const Test &t) { this->name = t.name; std::cout << name <<
#include <iostream>
#include <functional>
#include <thread>
#include <string>
class Test
{
public:
Test(const Test &t) { this->name = t.name; std::cout << name << ": copy constructor" << std::endl; }
Test(Test &&t) {this->name = std::move(t.name); std::cout << name << ": move contructor" << std::endl; }
Test(const std::string &name) {this->name=name;}
Test &operator=(const Test &t) { this->name = t.name; std::cout << name << ": copy operator = " << std::endl; return *this; }
Test &operator=(Test &&t) { this->name = std::move(t.name); std::cout << name << ": move operator = " << std::endl; return *this; }
std::string name;
};
class A
{
public:
void f(Test t1, Test t2)
{
std::cout << "running f" << std::endl;
}
void run()
{
std::cout << "running run" << std::endl;
Test t1("t1");
Test t2("t2");
auto functor = std::bind(&A::f, this, t1, std::placeholders::_1);
std::cout << "functor created by bind, t1 is passed into functor" << std::endl;
std::thread t(functor, t2);
std::cout << "thread created, functor and t2 passed into thread" << std::endl;
t.join();
}
};
int main()
{
A a;
a.run();
return 0;
}
然后,除了最后一个“t1拷贝”,所有内容都变为移动
(3) 为什么t1
仍被复制?这与第(2)项有关
如果我再换一行
void f(Test &t1, Test &t2)
然后它无法编译
(4) std::bind
&std::thread
的内部实现不是存储左值对象t1
&t2
?为什么调用测试&将失败?我很好奇标准是怎么说的
如果我把它改成
void f(const Test &t1, const Test &t2)
一切正常,最后两个t2
move和t1
copy被删除
(5) 我只想有人跟我确认一下这是否有效,并且没有挂起引用的危险,即使我们将线程t
存储在其他地方。例如,以下内容是否仍然有效
class A
{
public:
void f(const Test &t1, const Test &t2)
{
std::cout << "running f" << std::endl;
}
void run()
{
std::cout << "running run" << std::endl;
Test t1("t1");
Test t2("t2");
auto functor = std::bind(&A::f, this, std::move(t1), std::placeholders::_1);
std::cout << "functor created by bind, t1 is passed into functor" << std::endl;
std::thread t(std::move(functor), std::move(t2));
std::cout << "thread created, functor and t2 passed into thread" << std::endl;
t_internal.swap(t);
}
std::thread t_internal;
};
int main()
{
A a;
a.run();
a.t_internal.join();
return 0;
}
A类
{
公众:
无效f(常数测试和t1、常数测试和t2)
{
标准::cout
(1) 我很好奇为什么在函子和t2被传递到线程之前会有t2移动和t1移动
这是一个内部实现细节。thread
的构造函数将首先使用类似于std::bind
的内部绑定器绑定functor和提供的参数,然后将生成的绑定functor移动到分配给存储它的内存中
(2) 为什么在调用f()之前会有t2移动和t1复制
std::thread
执行INVOKE(decage\u COPY(std::forward(f))、decage\u COPY(std::forward(args))…)
始终返回一个右值,因此std::thread
将所有内容作为右值传递
同时,std::bind
将绑定参数作为左值传递,并将传递的内容完美地转发给其操作符()
。最终结果是,f
的第一个参数是从左值(因此是副本)构造的,而第二个参数是从右值(因此是移动)构造的
(3) 为什么t1仍然是拷贝?这与(2)有关
std::bind
将绑定参数作为左值传递
(4) bind&thread的内部实现存储的对象t1和t2不是左值吗?为什么调用Test&will会失败?我很好奇标准怎么说
第二个参数作为右值传递,它不绑定到测试&
(5) 我只想有人跟我确认一下这是否有效,并且没有挂起引用的危险,即使我们将线程t存储在其他地方
class A
{
public:
void f(const Test &t1, const Test &t2)
{
std::cout << "running f" << std::endl;
}
void run()
{
std::cout << "running run" << std::endl;
Test t1("t1");
Test t2("t2");
auto functor = std::bind(&A::f, this, std::move(t1), std::placeholders::_1);
std::cout << "functor created by bind, t1 is passed into functor" << std::endl;
std::thread t(std::move(functor), std::move(t2));
std::cout << "thread created, functor and t2 passed into thread" << std::endl;
t_internal.swap(t);
}
std::thread t_internal;
};
int main()
{
A a;
a.run();
a.t_internal.join();
return 0;
}
没关系。销毁std::thread
对象不会销毁线程的参数。它们将一直存在到线程终止。毕竟,detach()
需要工作。bind
将绑定参数作为左值传递,并将传递给它的操作符()的任何内容完美地转发
std::thread
将所有内容作为右值传递。最终结果是,f
的第一个参数需要复制,而第二个参数需要移动。@T.C.,谢谢。现在我很清楚了。我以为bind和thread有相同的行为。我不知道调用时它们是不同的。你能帮我看看吗问题(5)。我刚刚更新了它。我相信它是有效的,但只需要有人确认。谢谢。非常感谢。我现在很清楚。向std::bind和std::thread构造函数提供右值参数将把参数移动到内部存储中。调用函数时,std::bind将完美地将内部对象转发到函数std::thread将内部对象移动到函数中。另一个问题,如果传递std::ref(),std::thread不是有点危险吗。因为它在调用时会传递右值,那么原始对象将被销毁?经过一些测试,我认为如果将std::ref传递给线程,它在调用函数调用时将完美地向前传递。否则,它将传递右值。希望我没有弄错。谢谢!@user534498 No,bind
始终将内部对象作为左值传递s、 如果您传递了std::ref
,那么您保证在线程终止之前不会销毁引用的对象。然后,当调用线程的函数时,调用reference\u包装器的转换运算符以获取左值引用。