簡體   English   中英

依靠RVO最終功能

[英]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);
}

不過,我不認為這是必要的。 實際上,即使您不啟用優化,大多數編譯器也會復制橢圓。 gccclangiccMSVC都是這樣的例子。 這是因為標准明確允許復制橢圓。

如果您添加此代碼段:

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.

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