簡體   English   中英

在std :: bind和std :: thread中移動語義/行為

[英]move semantics/behaviors in std::bind and std::thread

請看下面的簡單測試程序,你可以只復制和測試。 我嘗試使用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)提供以下輸出

跑步

t1:復制構造函數

由bind創建的仿函數,t1被傳遞給仿函數

t2:復制構造函數

t1:復制構造函數

t2:移動構造函數

t1:移動構造函數

線程創建,仿函數和t2傳入線程

t2:移動構造函數

t1:復制構造函數

跑步

請注意粗體。

(1)我很好奇為什么在functort2傳遞到線程之前有t2移動t1移動

(2)為什么在調用f()之前有t2 MOVEt1 COPY

gcc的庫實現是否會進行一些優化以將COPY轉換為MOVE以提高效率? 例如,在調用f之前, t2被移入f(Test t1, Test t2)

如果我把上面兩行改成了,

auto functor = std::bind(&A::f, this, std::move(t1), std::placeholders::_1);
std::thread t(std::move(functor), std::move(t2));

然后一切都變得移動,除了最后一個“t1副本”。

(3)為什么還要復制t1 這與(2)有關。

如果我改變一條線,

void f(Test &t1, Test &t2)

然后它無法編譯。

(4) std::bindstd::thread的內部實現存儲對象t1t2是否是左值? 為什么打電話給測試&會失敗? 我很好奇標准說的是什么。

如果我改成它,

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

謝謝。

(1)我很好奇為什么在仿函數和t2傳遞到線程之前有t2移動和t1移動?

這是一個內部實現細節。 會發生什么是thread的構造函數將首先綁定仿函數並使用類似於std::bind的內部綁定器提供參數,然后將生成的綁定仿函數移動到分配用於存儲它的內存中。

(2)為什么在調用f()之前有t2 MOVE和t1 COPY?

std::thread執行INVOKE(DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...) DECAY_COPY總是返回一個右值,所以std::thread將所有內容都作為rvalue傳遞。

同時, std::bind將綁定參數作為左值傳遞,並將完全轉發的內容傳遞給其operator() 最終結果是f的第一個參數是由左值(因此是副本)構造的,而第二個參數是由右值構成的(因此是移動)。

(3)為什么t1仍然是復制? 這與(2)有關。

std::bind將綁定參數作為lvalues傳遞。

(4)是不是綁定&thread的內部實現存儲對象t1&t2哪個是左值? 為什么打電話給測試&會失敗? 我很好奇標准說的是什么。

第二個參數作為右值傳遞,並且不會綁定到Test &

(5)我只是希望有人向我確認這是否有效並且沒有懸掛引用的危險,即使我們將線程存儲在其他地方也是如此。 例如,以下是否仍然有效?

沒關系。 銷毀std::thread對象不會破壞線程的參數。 它們將一直存在直到線程終止。 畢竟, detach()需要工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM