2013-08-23 66 views
0

考慮一些功能:如何將unique_ptr與更通用的刪除器一起使用?

template<typename F> 
void foo(F f) { 
    std::unique_ptr<int> p = f(); 
    // do some stuff with p 
} 

因爲unique_ptr法令默認模板參數,default_delete,爲D,傳遞給foo任何函數對象返回與非默認刪除器未能編譯unique_ptr。例如,

int x = 3; 
foo([&x](){ 
    // use empty deleter 
    return std::unique_ptr<int>(&x, [](int*){}); 
}); 

但是,我可以看到這可能是有用的,我沒有看到它不應該成爲可能的直接原因。解決這個問題有沒有共同的方法?

編輯

的簡單的解決方法是定義foo而不是使用以下命令:

std::unique_ptr<int, std::function<void(int*)>> p = f(); 

但我不知道爲什麼,這可能不會被納入接口unique_ptr ?是否有類接口不能提供這個通用屬性的原因?有沒有辦法將這種東西「包裝」成新的定義?

例如,

template<typename T> 
using Generic_unique_ptr = 
    std::unique_ptr< 
    T, 
    std::function< void(typename std::unique_ptr<T>::element_type*) > 
    >; 

但因爲它暴露做類似的follwing的潛力,這似乎危險的,

Generic_unique_ptr<int> p(new int()); 

這將離開刪除器未初始化,並表現出不確定的行爲。也許某種方式可以提供作爲默認刪除器的實例std::default_delete<T>

+0

@TemplateRex我不確定它們是否一樣。如何從模板參數「F」推導刪除器類型? – jwalk

回答

4

如果您只想使用函數中的指針,則可以使用auto關鍵字 ;編譯器將演繹的unique_ptr 類型已被使用,因此自動做正確的事情:現在

template <typename F> 
void foo(F f) 
{ 
    auto p = f(); 
    p->bar(); 
} 

,從您的評論中,我們知道,這是不是你想要的,但你 希望能夠將unique_ptr存儲在您的班級中,以便稍後與 一起使用。這產生了一組完全不同的問題:

  1. unique_ptr<T, D1>unique_ptr<T, D2>是不同的類型。因此,我們需要知道什麼unique_ptr<T, D>會被你的仿函數F
  2. 即使我們知道的F返回類型提前回來了,我們班仍只能存儲unique_ptr<T, D1>,而不是unique_ptr<T, D2>

解決這個問題的最簡單的方法(即我能想到的,可能會有更好的 方式)是類型擦除

我們創建自己暴露在 unique_ptr管理的指針基類:

template <typename T> 
struct wrapper 
{ 
    virtual ~wrapper() {} 
    virtual T const * get() const = 0; 
    virtual T * get() = 0; 
}; 

從該類繼承我們的實際存儲類,其推導出類型的unique_ptr

template <typename T, typename F> 
struct storage 
    : wrapper<T> 
{ 
    storage(F f) { p_ = f(); } 
    T const * get() const { return p_.get(); } 
    T * get() { return p_.get(); } 

    private: 
     typename std::result_of<F()>::type p_; 
}; 

在您真正關心的類中,您現在可以存儲指向我們的基類的指針,並使用多態性訪問基礎對象,在這 的情況下unique_ptr。假設我們上面移動到 namespace detail類從用戶隱藏它們:

template <typename T> 
class some_class 
{ 
    public: 
     template <typename F> 
     void store(F f) 
     { 
      storage_.reset(new detail::storage<T, F>(f)); 
     } 

     T const * get() const { return storage_->get(); } 
     T * get() { return storage_->get(); } 

    private: 
     std::unique_ptr<detail::wrapper<T>> storage_; 
}; 

你可以找到一個完全工作的例子here

+0

太棒了!我一直都沒有給予這個關鍵詞太多的關注,但它很出色地解決了這個問題! – jwalk

+0

太棒了。我試着做類似的事情,並且慘敗。 – jwalk

+0

@jwalk更新了我的答案。 – nijansen

2

但我想知道爲什麼這不能被納入unique_ptr的接口?

因爲這樣做會迫使所有的std::function的開銷到大家unique_ptr旨在對於指針的單一所有權的任何情況非常有用。你支付你使用的東西;不是每個使用定製刪除程序的人都需要刪除程序爲通用程序。這樣,他們不必爲此付費。

此外,當前的方法允許它處理非指針資源,因爲刪除者可以精確指定在unique_ptr中存儲的類型。

如果要提供此通用刪除器構造,可以創建一個類(私下)從unique_ptr繼承並複製其接口,減去不帶有刪除器實例的構造器。這樣,用戶被迫在中傳遞刪除功能。

+0

感謝您的直接答覆。我會研究這個。 – jwalk

+0

+1爲什麼,-1表示某人應該從'std :: unique_ptr'繼承:爲什麼從'std'中的類型繼承,當一個成員變量也一樣? – Yakk

+0

@Yakk:因爲它不是「一樣好」。從一個繼承的類中導出一個接口比一個成員類更容易。 –

相關問題