【问题标题】:Lazy shared pointer - assignment operator惰性共享指针 - 赋值运算符
【发布时间】:2021-01-30 11:05:35
【问题描述】:

我创建了简单的惰性共享指针类。但是,目前我只能拥有它的单个实例,并且我的设计不支持复制分配。

/// <summary>
/// Simple lazy shared pointer
/// Pointer is initialized when first needed
/// 
/// Create new instance with CreateLazy static method
/// 
/// Copy is disabled, pointer can only be moved
/// If we would copy it not initialized
/// then two instances can be created
/// - from original and from copy
/// </summary>
template <class T>
class LazySharedPtr{
public:
        
    static LazySharedPtr<T> Create(){
        std::function<std::shared_ptr<T>()> customInit = [](){
            return std::make_shared<T>();
        };

        return LazySharedPtr(customInit);
    };

    template <typename ... Args>
    static LazySharedPtr<T> Create(Args ... args){
        return LazySharedPtr(std::forward<Args>(args) ...);
    };
    
    
    LazySharedPtr() :
        init(nullptr),
        ptr(nullptr){
    };

    LazySharedPtr(std::function<std::shared_ptr<T>()> customInit) :
        init(customInit),
        ptr(nullptr){
    };
    
    template <typename Y>
    LazySharedPtr(LazySharedPtr<Y> && other) :
        init(other.init),
        ptr(other.ptr){
        other.init = nullptr;
        other.ptr = nullptr;
    };

    LazySharedPtr(const LazySharedPtr& other) = delete;


    virtual ~LazySharedPtr() = default;

    T* operator->(){
        return InitAndGet().get();
    }

    const T* operator->() const{
        return InitAndGet().get();
    }

    T* operator*(){
        return InitAndGet().get();
    }

    const T* operator*() const{
        return InitAndGet().get();
    }

    explicit operator bool() const noexcept{
        return (ptr != nullptr);
    }

    explicit operator std::shared_ptr<T>() const{
        return InitAndGet();
    }

    template <typename U>
    friend class LazySharedPtr;

protected:    
    std::function<std::shared_ptr<T>()> init;

    mutable std::shared_ptr<T> ptr;

    template <typename ... Args>
    LazySharedPtr(Args ... args) :
        init([args = std::make_tuple(std::forward<Args>(args) ...)]() mutable {
        return std::apply(std::make_shared<T, Args...>, std::move(args));
    }),
        ptr(nullptr){
    };
    
    std::shared_ptr<T>& InitAndGet() const {
        if (!ptr) { ptr = init(); }
        return ptr;
    }
};

您有什么想法,如何改进它以支持复制分配?

目前的设计不支持这个:

class MyObject { };

LazySharedPtr<MyObject> t1 = LazySharedPtr<MyObject>::Create();
LazySharedPtr<MyObject> t2 = t1;

因为t2初始化后,t1不会被初始化。

我曾想过将内部shared_ptr 作为指向指针的指针并传递它。但是,使用原始指针,我必须管理引用计数并且做std::shared_ptr&lt;std::shared_ptr&lt;T&gt;&gt; 似乎很奇怪。还是没有?

你还有什么想法吗?

【问题讨论】:

  • Create() 的第一个重载采用模板参数,但从不使用它们。它实际上不能被调用——如果你明确指定模板参数,它会与第二个重载不明确;如果你不这样做,它不会被用于推断模板参数失败。
  • @IgorTandetnik 这是一个错字
  • 您通过值而不是通用引用来获取可变参数,因此您的所有std::forward 调用都是毫无意义的 - 无论如何都会复制参数。
  • struct ControlBlock {std::function&lt;std::shared_ptr&lt;T&gt;()&gt; init; std::shared_ptr&lt;T&gt; ptr;}; 存储shared_ptr&lt;ControlBlock&gt;,而不是shared_ptr&lt;T&gt;。那时复制将是微不足道的。注意:使这个东西线程安全可能会很棘手; std::call_once 可能会有所帮助。
  • @IgorTandetnik 有了这个,我在将孩子转换为父母时遇到了问题。 ControlBlock&lt;Child&gt; to ControlBlock&lt;Parent&gt; with assign ctor

标签: c++ c++17 shared-ptr lazy-initialization


【解决方案1】:

这是一个草图 - 未经测试,缺少应该易于填写的部分。我希望总体思路清晰。

template <class T>
class LazySharedPtr {
  struct ControlBlock {
    std::shared_ptr<T> ptr;
    std::function<std::shared_ptr<T>()> factory;
    std::shared_ptr<T> InitAndGet() {
      // Add thread safety here.
      if (!ptr) {
        ptr = factory();
        factory = nullptr;
      }
      return ptr;
    }
  };

  std::function<std::shared_ptr<T>()> init;
  // This member is not strictly necessary, it's just a cache.
  // An alternative would be to call `init` every time.
  std::shared_ptr<T> ptr;

public:
  // For exposition, assume all `T`s are constructible from `int`
  LazySharedPtr(int x) {
    auto control = std::make_shared<ControlBlock>();
    control->factory = [x]() { return std::make_shared<T>(x); };
    init = [control]() {return control->InitAndGet(); }
  }

  template <typename U>
  LazySharedPtr(const LazySharedPtr<U>& other)
      : ptr(other.ptr) {
    if (!ptr) {
      auto other_init = other.init;
      init = [other_init]() { return std::shared_ptr<T>(other_init()); };
    }  
  }

  std::shared_ptr<T> InitAndGet() {
    if (!ptr) {
      ptr = init();
      init = nullptr;
    }
    return ptr;
  }
};

基本上,一直键入擦除。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-13
    • 2012-01-11
    • 2020-08-17
    • 2021-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多