Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/138.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在std::bind和std::thread中移动语义/行为_C++_C++11_Gcc_Move Semantics_Stdthread - Fatal编程技术网

C++ 在std::bind和std::thread中移动语义/行为

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 <<

请看下面的简单测试程序,您可以直接复制和测试。我试过使用GCC4.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;
}
然后,除了最后一个“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包装器的转换运算符以获取左值引用。