2016-10-24 69 views
2

無論複製一個unique_ptr是否有意義*,我都試圖實現這種類,只是簡單地包裝一個std::unique_ptr,並在進行復制時遇到困難,在智能指針指向基礎的情況下並且存儲的對象是派生類。是否可以實現不受切片影響的copyable_unique_ptr?

一個天真的實現拷貝構造函數都可以在互聯網上找到(data是包裝std::unique_ptr):這裏

copyable_unique_ptr::copyable_unique_ptr(const copyable_unique_ptr& other) 
    : data(std::make_unique(*other.get()) // invoke the class's copy constructor 
{} 

問題是,由於離開了模板參數,是該副本創建類型爲T的實例,即使實際類型爲U : T。這會導致副本信息的丟失,雖然我完全理解這裏發生的原因,但我無法找到解決方法。

請注意,在移動的情況下,沒有問題。原始指針在用戶代碼的某處正確創建,並將其移至新所有者不會修改對象的實際類型。要製作副本,您需要更多信息。

另請注意,使用clone函數(因此感染T類型的接口)的解決方案並不是我所能接受的。


*如果你想有一個單一的擁有指針能夠複製的資源這可能有意義,它提供了比什麼scoped_ptrauto_ptr將提供更多。

+0

是否這樣? https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern#Polymorphic_copy_construction – Hayt

+0

@Hayt:請閱讀我上面的最後一句話。 – rubenvb

+0

我的意思是你會在'T'和'U'之間引入一個新類型。因此不直接感染T. – Hayt

回答

1

在爲了讓一個好的C++編譯器對代碼感到滿意而苦苦掙扎之後,我對這些語義感到滿意,我向你們展示了一個(非常準確的)value_ptr,移動語義。重要的是要記住使用make_value<Derived>,因此它會選擇正確的複製功能,否則複製將切分您的對象。我沒有找到deep_copy_ptrvalue_ptr的實現,它實際上具有可以承受切片的機制。這是一個粗獷的實現,錯過的東西像細粒度參考處理或陣列專業化,但在這裏它仍然是:

template <typename T> 
static void* (*copy_constructor_copier())(void*) 
{ 
    return [](void* other) 
     { return static_cast<void*>(new T(*static_cast<T*>(other))); }; 
} 

template<typename T> 
class smart_copy 
{ 
public: 
    using copy_function_type = void*(*)(void*); 

    explicit smart_copy() { static_assert(!std::is_abstract<T>::value, "Cannot default construct smart_copy for an abstract type."); } 
    explicit smart_copy(copy_function_type copy_function) : copy_function(copy_function) {} 
    smart_copy(const smart_copy& other) : copy_function(other.get_copy_function()) {} 
    template<typename U> 
    smart_copy(const smart_copy<U>& other) : copy_function(other.get_copy_function()) {} 

    void* operator()(void* other) const { return copy_function(other); } 
    copy_function_type get_copy_function() const { return copy_function; } 

private: 
    copy_function_type copy_function = copy_constructor_copier<T>(); 
}; 

template<typename T, 
     typename Copier = smart_copy<T>, 
     typename Deleter = std::default_delete<T>> 
class value_ptr 
{ 
    using pointer = std::add_pointer_t<T>; 
    using element_type = std::remove_reference_t<T>; 
    using reference = std::add_lvalue_reference_t<element_type>; 
    using const_reference = std::add_const_t<reference>; 
    using copier_type = Copier; 
    using deleter_type = Deleter; 

public: 
    explicit constexpr value_ptr() = default; 
    explicit constexpr value_ptr(std::nullptr_t) : value_ptr() {} 
    explicit value_ptr(pointer p) : data{p, copier_type(), deleter_type()} {} 

    ~value_ptr() 
    { 
    reset(nullptr); 
    } 

    explicit value_ptr(const value_ptr& other) 
    : data{static_cast<pointer>(other.get_copier()(other.get())), other.get_copier(), other.get_deleter()} {} 
    explicit value_ptr(value_ptr&& other) 
    : data{other.get(), other.get_copier(), other.get_deleter()} { other.release(); } 
    template<typename U, typename OtherCopier> 
    value_ptr(const value_ptr<U, OtherCopier>& other) 
    : data{static_cast<pointer>(other.get_copier().get_copy_function()(other.get())), other.get_copier(), other.get_deleter()} {} 
    template<typename U, typename OtherCopier> 
    value_ptr(value_ptr<U, OtherCopier>&& other) 
    : data{other.get(), other.get_copier(), other.get_deleter()} { other.release(); } 

    const value_ptr& operator=(value_ptr other) { swap(data, other.data); return *this; } 
    template<typename U, typename OtherCopier, typename OtherDeleter> 
    value_ptr& operator=(value_ptr<U, OtherCopier, OtherDeleter> other) { std::swap(data, other.data); return *this; } 

    pointer operator->() { return get(); } 
    const pointer operator->() const { return get(); } 

    reference operator*() { return *get(); } 
    const_reference operator*() const { return *get(); } 

    pointer get() { return std::get<0>(data); } 
    const pointer get() const { return std::get<0>(data); } 

    copier_type& get_copier() { return std::get<1>(data); } 
    const copier_type& get_copier() const { return std::get<1>(data); } 
    deleter_type& get_deleter() { return std::get<2>(data); } 
    const deleter_type& get_deleter() const { return std::get<2>(data); } 

    void reset(pointer new_data) 
    { 
    if(get()) 
    { 
     get_deleter()(get()); 
    } 
    std::get<0>(data) = new_data; 
    } 

    pointer release() noexcept 
    { 
    pointer result = get(); 
    std::get<0>(data) = pointer(); 
    return result; 
    } 

private: 
    std::tuple<pointer, copier_type, deleter_type> data = {nullptr, smart_copy<T>(), std::default_delete<T>()}; 
}; 

template<typename T, typename... ArgTypes> 
value_ptr<T> make_value(ArgTypes&&... args) 
{ 
    return value_ptr<T>(new T(std::forward<ArgTypes>(args)...));; 
} 

代碼存在here和測試,以表明它應該如何爲大家工作都here看看自己。評論總是歡迎。

+0

切片保護簡直不值得這個類對每個'value_ptr'實例施加的開銷。 –

+0

@Nicol如果你有一個替代解決方案,只有基類指針的問題,並有可能做出一個非切片的副本,我都耳熟能詳。 – rubenvb

相關問題