2016-10-24 11 views
2

unique_ptr을 복사하는 것이 의미가 있는지에 관계없이 *이 종류의 클래스를 구현하기 위해 간단하게 std::unique_ptr을 랩핑하고 기지로의 스마트 포인터의 경우 복사가 수행되는 곳에서 정확하게 어려워졌습니다. 상기 저장된 객체는 파생 클래스이다.슬라이싱의 영향을받지 않는 copyable_unique_ptr을 구현할 수 있습니까?

복사 생성자의 순진 구현 (datastd::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 
{} 

문제, 즉 인해 왼쪽으로 템플릿 인수에 있다는 것입니다 복사 실제 유형이 U : T 인 경우에도 T 유형의 인스턴스가 생성됩니다. 이것은 복사본에 대한 정보의 손실을 가져 오는데, 왜 이것이 여기서 일어나는지를 완벽하게 잘 알고 있지만이 문제를 해결할 방법을 찾을 수는 없습니다.

이동 케이스에는 아무런 문제가 없습니다. 원래 포인터가 사용자 코드의 어딘가에서 제대로 작성되었으며이를 새 소유자로 이동해도 오브젝트의 실제 유형은 수정되지 않습니다. 사본을 만들려면 더 많은 정보가 필요합니다.

clone 기능을 사용하는 솔루션 (따라서 T 유형의 인터페이스에 감염 됨)은 내가 받아 들일 수있는 것이 아닙니다.


* 당신이 이해 할 수있는 복사 가능한 자원에 하나의 소유 포인터를 원하고 그것이 scoped_ptr 또는 auto_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_ptr 또는 value_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