2014-07-14 107 views
8

這是我經常遇到的RAII問題。我想知道是否有人有一個好的解決方案。RAII和推導出的模板參數

開始用你的標準RAII實用類:

class RAIIHelper { 
    RAIIHelper() { 
    AcquireAResource(); 
    } 
    ~RAIIHelper() { 
    ReleaseTheResource(); 
    } 
}; 

現在,由於種種原因,我需要使它成爲一個模板。我們還假設其構造函數模板參數類型的參數:

template <typename T> 
class RAIIHelper { 
    RAIIHelper(T arg) { 
    AcquireAResource(); 
    } 
    ~RAIIHelper() { 
    ReleaseTheResource(); 
    } 
}; 

現在考慮使用場所:

void func() { 
    RAIIHelper<SomeType> helper(someObj); 
} 

這很煩人有寫出來SomeType時,它可以從someObj推斷,所以我寫了一個輔助函數來推斷類型:

template <typename T> 
RAIIHelper<T> makeRAIIHelper(T arg) { 
    return RAIIHelper<T>(arg); 
} 

現在我可以用它像這樣:

void func() { 
    auto helper = makeRAIIHelper(someObj); 
} 

很好,對不對?除了有一個障礙:RAIIHelper現在需要是可複製或可移動的,而釋放資源的析構函數可能會被調用兩次:一次爲makeRAIIHelper返回的臨時值,一次爲調用函數中的局部變量。

實際上,我的編譯器執行RVO,析構函數只被調用一次。但是,這不能保證。從這個事實可以看出,如果我試圖給RAIIHelper一個= delete'd移動構造函數,代碼不再編譯。

我可以向RAIIHelper添加額外的狀態,以便它知道在移動之後不會調用ReleaseTheResource(),但這是在添加makeRAIIHelper()以獲得類型扣除之前不必要的額外工作。

有沒有一種方法可以得到類型扣除,而不必爲RAIIHelper增加額外的狀態?

+0

在旁邊,看起來你正在重新創造'unique_ptr',你確定絕對沒有天生的'SomeType'類型的哨兵對象嗎? – Deduplicator

+0

你可以使用'unique_ptr'。 –

+0

編寫適當的移動構造函數並禁用複製構造函數。這應該不成問題。 –

回答

8

有一個非常簡單的解決方案:使用對臨時對象的引用,而不是將其複製到局部變量中。在以前的答案和評論

void func() 
{ 
    auto&& helper = makeRAIIHelper(someObj); 
} 
+5

如果你用'直接初始化'來替換'return RAIIHelper (arg);'''返回{arg}',那麼這個類型不需要是可移動的。 – dyp

+0

當臨時對象超出範圍時,下一行將如何工作? –

+4

@ StianV.Svedenborg該臨時的生命週期延長到範圍的末尾。 – dyp

1

大廈:

你可以離開可移動到的unique_ptr的責任,並返回你的資源是這樣的:

template <class T> 
auto makeRAII(T arg) -> std::unique_ptr<RAIIHelper> { 
    return make_unique(RAIIHelper<T>(arg)); 
} 

現在,它的作用域像一個靜態可變的,但可能是不可移動的,不可複製的&。

+0

這有效,但它強加了不必要的動態分配的開銷。 – HighCommander4

+0

根據對象的生命週期和開發者的懶惰(這是一件好事),Aye可能會也可能不值得。 –