2013-11-01 58 views

回答

11

您可以編寫自己的包裝函數模板:

// Precondition: !container.empty() 
// Exception safety: If there is an exception during the construction of val, 
//     the container is not changed. 
//     If there is an exception during the return of the value, 
//     the value is lost. 
template <typename C> 
auto back_popper(C & container) -> decltype(container.back()) 
{ 
    auto val(std::move(container.back())); 
    container.pop_back(); 
    return val; 
} 

用法:

auto element = back_popper(myDeque); 

不能圍繞激勵擺在首位的back()pop_back()分離的根本問題得到哪些是元素的複製或移動構造函數可能會拋出異常,並且在發生這種情況時可能會丟失彈出的元素。您可以通過返回一個非投擲對象來緩解它,例如,一個unique_ptr,這將交易掉動態分配元素的風險,但顯然這是你必須作出的標準不適合你的個人選擇。

例如:

// Guarantees that you either get the last element or that the container 
// is not changed. 
// 
template <typename C> 
auto expensive_but_lossless_popper(C & container) 
-> typename std::unique_ptr<decltype(container.back())> 
{ 
    using T = decltype(container.back()); 

    std::unique_ptr<T> p(new T(std::move(container.back()))); 
    container.pop_back(); 
    return p;    // noexcept-guaranteed 
} 

編輯:我加繞back()電話std::move,作爲@simple建議。這是合法的,因爲不再需要移動元素,而且許多現實世界的類不帶移動構造函數,因此覆蓋了大量情況,「無損」解決方法僅爲少量沒有任何異動的「奇怪」類型移動。

+2

'back_popper'這個名字聽起來更像是一個類,而不是一個函數IMO。 – Simple

+0

當我發佈我的答案後,我看到了你的編輯,並用代碼和評論來解釋它,所以:+1 –

+0

@DanielFrey:我進一步解釋了另一種解決方案:-S –

10

在設計上,C++沒有提供這樣一種機制,開箱即用,以確保異常安全

當您嘗試實現它,您首先進行元素的副本,然後在彈出的容器和最後你想把對象返回給調用者。但是當最後一個操作的拷貝構造函數拋出異常時會發生什麼?該對象不再位於容器中,作爲調用者,您沒有收到它的副本。爲了避免這種情況併爲容器的操作提供強大的異常保證,返回同時彈出的元素的操作不會直接進行。

+0

在C++ 11中,我們沒有辦法確定承諾不會在複製構造時拋出的對象嗎?然後,該庫可以爲這些元素類型提供複製彈出和返回方法。 – Walter

+0

@Walter在理論上這是可能的,你甚至可以根據'std :: is_nothrow_copy_constructible :: value'將'返回類型從'void'改變爲'T'作爲'pop_back'。或者你可以更進一步,使用移動操作和'std :: is_nothrow_move_constructible :: value'。但是,正如你所看到的,需要考慮的事情的數量(可複製和/或可移動的類型)並不是微不足道的,AFAIK沒有人撰寫論文來表明它。 –

+0

@Walter從Anthony Williams的[C++ Concurrency in Action]的3.2.3節中的線程安全角度更詳細地討論了您的建議(http://www.amazon.com/C-Concurrency-Action-Practical -Multithreading/DP/1933988770)。 – TemplateRex

4

如果你不想失去的異常安全,你可以限制使用SFINAE這種方法:

// 
// Precondition: !container.empty() 
//    
template <typename C> 
auto back_popper(C & container) 
-> typename std::enable_if<std::is_nothrow_move_constructible< 
          decltype(container.back())>::value, 
          decltype(container.back())>::type 
{ 
    auto val = std::move(container.back()); 
    container.pop_back(); 
    return val; 
} 

static_assert(的工作,爲自己)。 (注意:如果有移動構造函數,請編輯以避免任何副本,請按照Simple的註釋。

+0

這可能應該是'std :: is_nothrow_move_constructible'。 – Simple

+0

@Simple是的,但它可能不可移動,仍然不可複製...允許所有可能發生(我認爲),請參閱編輯。 – Walter

+0

如果類型不可移動,那麼'std :: is_nothrow_move_constructible'將檢查它是否不是可複製的,而是由於它是如何定義的。請注意'return std :: move(val)'防止複製elision。你應該寫'return val'。 – Simple

相關問題