[英]Relying on RVO for finally function
閱讀C ++編程語言 (第4版),在“異常處理”一章中,有一個用於臨時清理代碼的示例幫助:
template<typename F>
struct Final_action {
Final_action(F f): clean{f} {}
~Final_action() { clean(); }
F clean;
};
template<class F>
Final_action<F> finally(F f)
{
return Final_action<F>(f);
}
它用得像
auto act1 = finally([&]{ delete p; });
在聲明act1的塊的末尾運行lambda代碼。
由於返回值優化將Final_action<>
限制為單個實例,我認為這在Stroustrup測試時Final_action<>
了 - 但是RVO不是一個可選的優化嗎? 如果在從finally返回時復制實例,那么顯然~Final_action()
將為兩個副本運行。 換句話說, p
被刪除兩次。
這種行為是否被標准中的某些東西阻止了,或者代碼是否足夠簡單以便大多數編譯器對其進行優化?
實際上,這個例子依賴於復制橢圓,這只能保證(在某些環境中)自C ++ 17以來。
話雖如此,copy ellision是在大多數現代C ++ 11 / C ++ 14編譯器中實現的優化。 如果此片段在優化版本上失敗,我會感到驚訝。
但是,如果你想讓它變得無懈可擊,你可以添加一個移動構造函數:
template<typename F>
struct Final_action {
Final_action(F f): clean{f} {}
~Final_action() { if (!moved) clean(); }
Final_action(Final_action&& o) : clean(std::move(o.clean)) {o.moved = true;}
private:
F clean;
bool moved{false};
};
template<class F>
Final_action<F> finally(F f)
{
return Final_action<F>(f);
}
不過,我不認為這是必要的。 實際上,即使您不啟用優化,大多數編譯器也會復制橢圓。 gcc , clang , icc和MSVC都是這樣的例子。 這是因為標准明確允許復制橢圓。
如果您添加此代碼段:
int main() {
int i=0;
{
auto f = finally([&]{++i;});
}
return i;
}
並分析godbolt.org上生成的程序集輸出,你會看到Final_action::~Final_action()
通常只被調用一次(在main()
)。 啟用優化,編譯器更積極:檢查從GCC 4.7.1輸出僅-O1
啟用:
main:
mov eax, 1 # since i is incremented only once, the return value is 1.
ret
這只適用於C ++ 17! 使用C ++ 11或C ++ 14時,由於刪除了復制構造函數,它會失敗。 從C ++ 17開始,有些情況會強制執行RVO ,因此不需要復制/移動構造函數。
如果實例被復制[..]
好吧,如何禁止復制?
template<typename F>
struct Final_action {
Final_action(F f): clean{f} {}
Final_action(Final_action const &) = delete;
Final_action & operator=(Final_action const &) = delete;
~Final_action() { clean(); }
F clean;
};
或者從boost::noncopyable
派生,如果你已經使用了boost。
#include <iostream>
template<typename F>
struct Final_action {
Final_action(F f): clean{f} {}
Final_action(Final_action const &) = delete;
Final_action & operator=(Final_action const &) = delete;
~Final_action() { clean(); }
F clean;
};
template<class F>
Final_action<F> finally(F f)
{
return Final_action<F>(f);
}
int main() {
auto _ = finally([]() { std::cout << "Bye" << std::endl; });
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.