簡體   English   中英

By-ref參數:這是std :: thread和std :: bind之間的不一致嗎?

[英]By-ref arguments: is this an inconsistency between std::thread and std::bind?

std::bindstd::thread共享一些設計原則。 由於它們都存儲了與傳遞的參數對應的本地對象,因此如果需要引用語義,我們需要使用std::refstd::cref

void f(int& i, double d) { /*...*/ }

void g() {
    int x = 10;
    std::bind(f, std::ref(x), _1) (3.14);
    std::thread t1(f, std::ref(x), 3.14);
    //...
}

但是我對最近的個人發現很感興趣: std::bind將允許你在上面的情況下傳遞一個值,即使這不是人們通常想要的。

    std::bind(f, x, _1) (3.14); // Usually wrong, but valid.

但是,對於std::thread ,情況並非如此。 以下將觸發編譯錯誤。

    std::thread t2(f, x, 3.14); // Usually wrong and invalid: Error!

乍一看,我認為這是一個編譯器錯誤,但錯誤確實合法。 似乎由於30.3.1.2強加的復制衰減 要求 (轉換int& in int ),模板化版本的std::thread的構造函數無法正確推導出參數。

問題是:為什么不要求類似於std::bind的參數呢? 或者這是否明顯不一致?

注意:解釋為什么它在下面的評論中不重復。

bind返回的函數對象是為了重用而設計的(即調用被多次調用); 因此,它必須將其綁定參數作為左值傳遞,因為您不希望從所述參數移動或稍后調用將看到移動的綁定參數。 (同樣,您也希望將函數對象作為左值調用。)

這個問題不適用於std::thread和朋友。 線程函數只能使用提供的參數調用一次。 離開它們是完全安全的,因為沒有別的東西可以看着它們。 它們實際上是臨時副本,僅用於新線程。 因此,函數對象被稱為rvalue,參數作為rvalues傳遞。

由於lambdas的存在, std::bind在它到達時大多已經過時了。 使用C ++ 14改進和C ++ 17 std::applybind的剩余用例幾乎消失了。

即使在C ++ 11中, bind解決了lambda在相對罕見的情況下無法解決的問題。

另一方面, std::thread正在解決一個稍微不同的問題。 它不需要靈活的bind來“解決每個問題”,相反它可以阻止通常是壞代碼。

bind情況下,傳遞給f的引用不是x ,而是對x的內部存儲副本的引用。 這非常令人驚訝。

void f(int& x) {
    ++x;
    std::cout << x << '\n';
};

int main() {
    int x = 0;
    auto b = std::bind(f, x);
    b();
    b();
    b();
    std::cout << x << '\n';
}

版畫

1
2
3
0

其中最后的0是原始的x ,而1 23是存儲在fx的遞增副本。

使用lambda,可以明確可變存儲狀態和外部引用之間的差異。

auto b = [&x]{ f(x); };

VS

auto b = [x]()mutable{ f(x); };

其副本中的一個x然后調用f反復就可以了,其它的引用傳遞到xf

實際上沒有辦法用bind執行此操作,而不允許f訪問存儲的x副本作為引用。

對於std::thread ,如果你想要這個可變的本地拷貝行為,你只需要使用lambda。

std::thread t1([x]()mutable{ f(x); });

事實上,我認為C ++ 11中的大部分INVOKE語法似乎都是沒有C ++ 14 power lambdas和std::apply的遺留問題。 很少有案例沒有通過lambda和std::apply解決(需要應用,因為lambda不能輕易支持將包移入其中然后在里面進行處理)。

但是我們沒有時間機器,所以我們有這些多種並行方式來表達在C ++中在特定上下文中調用某些東西的想法。

從我所知道的, thread開始時基本上與bind相同的規則,但是在2010年被N3090修改為接受你已經識別的約束。

使用它來平分各種貢獻,我相信你正在尋找LWG問題929

具有諷刺意味的是,其目的似乎是使thread構造函數受到較少限制。 當然沒有提到bind ,雖然這個措辭后來也適用於async (LWG 1315之后的“清理”部分),所以我會說bind被拋在后面。

但是,很難確定,所以我建議問委員會本身

暫無
暫無

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

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