2016-02-28 70 views
2

假設我想編程接收器容器只移動 類型。根據Meyer和Sutter對 http://scottmeyers.blogspot.de/2014/07/should-move-only-types-ever-be-passed.html 的討論,sink參數應該按值傳遞,然後移入 成員。但就異常安全性而言,這聽起來對我來說似乎是一個不好的 想法。當構造函數拋出異常時,接收器參數的值將丟失,而當我通過右值引用並且在實際移動之前發生異常時,調用者仍然可以使用 檢索sink參數的值。下面接收器參數和例外

例子:

#include <memory> 
#include <iostream> 
#include <stdexcept> 

using widget = std::unique_ptr<int>; 

struct sink_1 { 
    sink_1(widget&& w) { 
     throw std::runtime_error("error"); 
     w_ = std::move(w); 
    } 
    widget w_; 
}; 

struct sink_2 { 
    sink_2(widget w) { 
    throw std::runtime_error("error"); 
    w_ = std::move(w); 
    } 
    widget w_; 
}; 

int main(int argc, char *argv[]) 
{ 

    auto w1 = std::make_unique<int>(10); 
    auto w2 = std::make_unique<int>(20); 

    try { 
     sink_1 s1(std::move(w1)); 
    } catch (std::runtime_error &e) { 
     std::cout << *w1 << std::endl; 
    } 

    try { 
     sink_2 s2(std::move(w2)); 
    } catch (std::runtime_error &e) { 
     std::cout << *w2 << std::endl; // crashes 
    } 


    return 0; 
} 

這是一個有效的和好的設計?

+0

只是爲了給網站他們當之無愧的信用:如果你有完整的工作代碼,你的問題實際上可能更適合[SE代碼審查](http://codereview.stackexchange.com/)。 –

回答

3

說「好吧,我不同意Scott Meyers。」

這兩個例子在所謂的「例外保證」中有所不同。

sink_1構造具有強大的異常保證 - 這意味着如果一個異常被拋出,w將保持不變。

sink_2構造具有弱異常保證 - 這意味着如果一個異常被拋出 - 我們保證,也不s2也不w將泄漏的任何資源。

在我看來,堅持最高的例外保證是最好的,在這種情況下,我會堅持以sink_1爲例。

這不是,sink_2是壞的,我個人會選擇第一個版本。例如,我的程序可以是一個Web服務器。 w可能是傳入的HTTP請求。如果拋出異常,使用sink_2將以非常積極的方式簡單地關閉請求。 sink_1不同,將允許我再次重試,使用不同的代碼路徑,甚至給我發送一條體面的消息給客戶說可能失敗的可能性。

0

不,這不是好設計。

您對w1所做的操作可能會或可能不會崩潰,具體取決於在w1變爲「移出」之前還是之後拋出異常。

一旦您在w2上調用std::move並將其值傳遞給sink2::sink_2,它就會從「移出」(進入函數的參數)。訪問w2保證會崩潰。

你應該總是把一個調用std :: move作爲一個指示器,表明這些值將會消失,而不是以任何方式使用它(這就是爲什麼std :: unique_ptr優於std :: auto_ptr的原因:當你按值傳遞一個auto_ptr,它看起來像一個副本,但它的行爲像一個動作,你不能通過值傳遞unique_ptr(它沒有拷貝構造函數),並且要從一個名爲unique_ptr的地方移動,你必須使用std :: move )。

+0

如果您的訪問沒有任何先決條件,那麼訪問從「unique_ptr」移動是安全的。例如,針對'nullptr'和'get()'的測試沒有任何先決條件。有關更多詳細信息,請參閱http://stackoverflow.com/a/7028318/576911。 –