2016-10-24 9 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 
{} 

問題は、取り残さテンプレート引数に、そのコピーであること、です実数型がU : Tであっても、タイプTのインスタンスが作成されます。これはコピーの情報が失われることにつながりますが、なぜこのようなことが起こるのかを完全に理解していますが、これを回避する方法はありません。

移動の場合、問題はありません。元のポインタはユーザコードのどこかで正しく作成され、新しい所有者に移動してもオブジェクトの実タイプは変更されません。コピーを作成するには、より多くの情報が必要です。

また、機能を使用するソリューション(したがって、タイプ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_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

@ニコル基本クラスのポインタと非スライスコピーを作成する可能性があるという問題に対する別の解決法がある場合、私はすべての耳にします。 – rubenvb

関連する問題