2014-09-04 50 views
9

我有一個函數可以處理作爲接收器參數傳入的大量數據。我BigData類型已經是C++ 11感知,並配有功能齊全的移動構造函數和移動分配的實現,這樣我就可以脫身,而無需複製該死的東西:Sink參數並移動可能失敗的函數的語義(強壯的異常安全性)

Result processBigData(BigData); 

[...] 

BigData b = retrieveData(); 
Result r = processBigData(std::move(b)); 

這一切工作完全正常。但是,我的處理函數可能會在運行時偶爾失敗,從而導致異常。這不是一個真正的問題,因爲我只能修復內容並重試:

BigData b = retrieveData(); 
Result r; 
try { 
    r = processBigData(std::move(b)); 
} catch(std::runtime_error&) { 
    r = fixEnvironmnentAndTryAgain(b); 
    // wait, something isn't right here... 
} 

當然,這是行不通的。

由於我移動了我的數據進入處理函數,到達異常處理程序時,b將不再可用。

這樣做可能會大大降低我對按值收取匯率參數的熱情。

所以這裏是一個問題:如何在現代C++代碼中處理這種情況?如何檢索先前移入未能執行的函數的數據訪問?

您可以根據需要更改BigDataprocessBigData的實現和接口。然而,最終的解決方案應該儘量減少原有代碼在效率和可用性方面的缺陷。

+0

重要的問題是,結果是否包含b的移動資源或僅基於它? – IdeaHat 2014-09-04 14:05:41

+0

@MadScienceDreams「Result」只是從'b'計算出來的,它並不包含對原始'b'的引用或拷貝。 – ComicSansMS 2014-09-04 14:06:56

+0

@ComicSansMS但它是否包含移動(而不是複製)的內容? – Potatoswatter 2014-09-04 14:09:05

回答

2

我對這個問題同樣不感興趣。

據我所知,最好的當前習慣用法是將傳值分成一對傳遞引用。

template< typename t > 
std::decay_t<t> 
val(t && o) // Given an object, return a new object "val"ue by move or copy 
    { return std::forward<t>(o); } 

Result processBigData(BigData && in_rref) { 
    // implementation 
} 

Result processBigData(BigData const & in_cref) { 
    return processBigData(val(in_cref)); 
} 

當然,參數的零碎可能已經在異常之前被移動了。問題傳播到任何processBigData調用。

我有一個靈感來開發一個對象,在某些例外情況下它會自動回到它的源頭,但這是解決我的項目中某個特定問題的方法。它可能最終變得過於專業化,或者根本不可行。

+0

我仍然不確定你爲什麼要傳遞一個RHR函數,如果它實際上並不消耗BigData的話。我想讓它在將來消費它?另外,你在不可複製類型的情況下做什麼(如'std :: unique_ptr')? – IdeaHat 2014-09-04 14:19:32

+0

是的,增加rvalue refs的重載在這裏的確會有所幫助。您可以推遲從'BigData'移動到可以保證不會發生異常的點。當然,這受到了必須在函數接口中引入rvalue-ref重載的常見弊端(因此我並不是100%相信它滿足了我的可用性約束),但是在某些情況下它可能實際上工作得很好。 +1方式。 – ComicSansMS 2014-09-04 14:29:52

+0

@MadScienceDreams 1.是的,考慮到問題中的澄清評論,我不確定它爲什麼不是一個「const」,但我只是在概念上回答了這個問題。 2.不可複製的類型不需要'const&'重載,就這些了。 – Potatoswatter 2014-09-04 15:05:49

2

很顯然,這個問題在最近的CppCon 2014上進行了熱烈的討論。Herb Sutter在結束語中概括了最新狀態Back to the Basics! Essentials of Modern C++ Style (slides)

他的結論很簡單:不要對接收器參數使用傳值。

首先使用這種技術的論點(如Eric Niebler的Meeting C++ 2013主題演講C++11 Library design (slides)所推廣的)似乎被缺點所掩蓋。通過使用const&/&&來消除因功能超載而導致的組合爆炸,通過值傳遞接收器參數的初始動機是。

不幸的是,這似乎帶來了一些意想不到的後果。其中之一是潛在的效率缺陷(主要是由於不必要的緩衝區分配)。另一個是這個問題的異常安全問題。這兩個都在Herb的談話中討論過。

香草的結論是使用傳址值水槽參數,而是依靠單獨const&/&&(與const&是對於那些需要優化少數情況下保留默認值,&&)。

這也與@Potatoswatter's answer建議的相符。通過通過&&傳遞sink參數,我們可能會推遲數據從參數的實際移動到可以給予noexcept保證的點。

我有點喜歡按照價值傳遞水槽參數的想法,但它似乎在實踐中並沒有像所有人希望的那樣好。