请看下面的简单测试程序,您只需复制并进行测试。我已经使用了gcc 4.9
来编译它,编译正常。
#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;
}
程序在gcc 4.9 (mingw)下的输出如下:
请注意粗体字。运行 run
t1:复制构造函数
由bind创建的函数对象,t1被传入函数对象
t2:复制构造函数
t1:复制构造函数
t2:移动构造函数
t1:移动构造函数
线程已创建,函数对象和t2被传入线程
t2:移动构造函数
t1:复制构造函数
运行 f
(1)我想知道为什么在将函数对象和t2传入线程之前会有t2 move和t1 move?
(2)以及为什么在调用
f()
之前会有t2 MOVE和t1 COPY?gcc的库实现是否进行了某些优化以将COPY转换为MOVE以提高效率?例如,在调用
f(Test t1, Test t2)
之前,将t2
移动到其中?如果我将上述两行更改为:
auto functor = std::bind(&A::f, this, std::move(t1), std::placeholders::_1);
std::thread t(std::move(functor), std::move(t2));
接下来的一切都会被移动,除了最后一个“t1 copy”。
(3) 为什么t1
还是被复制了?这与(2)有关。
如果我再改变一行,
void f(Test &t1, Test &t2)
然后它无法编译。
(4) std::bind
和std::thread
的内部实现不是存储左值对象t1和t2吗?为什么调用Test会失败?我很好奇标准是怎么说的。
如果我把它改成:
void f(const Test &t1, const Test &t2)
一切都正常工作,最后两个t2
移动和t1
复制被消除了。
(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;
}
感谢您的选择。
bind
将绑定的参数作为左值传递,并完美地转发传递给其operator()
的任何内容。std::thread
将所有内容都作为右值传递。净结果是f
的第一个参数需要被复制,而第二个参数则被移动。 - T.C.