具有共享所有者语义的容器

Posted

技术标签:

【中文标题】具有共享所有者语义的容器【英文标题】:Container with shared owner semantics 【发布时间】:2017-02-25 23:40:14 【问题描述】:

我正在设计一种具有共享所有权语义的容器类型。它支持切片,因此切片共享所有权。我的问题之一是数据共享似乎会干扰 const 的正确性,所以我试图注意这一点,但我对结果不满意。

以下是我的实际代码的一个大大分解的版本:

#include <memory>
#include <vector>
#include <algorithm>

template <typename T>
class SharedMem

public:
    SharedMem(std::initializer_list<T> init)
    : m_mem(std::make_shared<std::vector<T>>(init.begin(), init.end()))
    , m_data(m_mem->data())
    , m_size(m_mem->size())
     

    SharedMem(SharedMem& other) = default;        // best-effort for copy-construction
    SharedMem(SharedMem const& other) = delete;   // disallow, would circumvent const-correctness

    SharedMem& operator = (SharedMem const& other) 
        std::copy(other.m_data, other.m_data + other.m_size, m_data);
        return *this;
    

    std::size_t size() const
     return m_size; 
    T& operator [] (std::size_t index)
     return m_data[index]; 
    T const& operator [] (std::size_t index) const
     return m_data[index]; 

    SharedMem slice(std::size_t first, std::size_t last) 
        SharedMem<T> ret(*this);
        ret.m_data += first;
        ret.m_size = last - first;
        return ret;
    
    SharedMem const slice(std::size_t first, std::size_t last) const 
        SharedMem<T> ret(*this);
        ret.m_data += first;
        ret.m_size = last - first;
        return ret;
    

private:
    std::shared_ptr<std::vector<T>> m_mem;   // shared underlying memory
    T* m_data;                               // start of slice
    std::size_t m_size;                      // size of slice
;

预期用途:

int main(int argc, char** argv) 
    SharedMem<int> a  0, 1, 2, 3, 4 ;
    SharedMem<int> b  8, 9 ;
    SharedMem<int> c = a;   // shallow copy of a, data is shared
    a.slice(1, 3) = b;      // a = [0, 8, 9, 3, 4]
    c[4] = 6;               // a = [0, 8, 9, 3, 6]

有些东西告诉我我走错了路。我发现我的方法存在以下问题:

它违反了规则 3。我特别不喜欢为了修复 const 正确性而禁用默认复制构造函数的需要。否则,可以创建 const 对象的非常量副本,而后者可以修改前者的元素。 复制构造和赋值实现了非常不同的操作。这就是我如何让 c = aa.slice(1, 3) = b 做正确的事情(实际上是非常不同的事情)。

我不确定我是否遇到了麻烦。问题:

这种设计是否可行,还是会在未来引起问题?如果有,是哪个? 如果存在严重缺陷,如何修复/避免?

感谢任何提示。

【问题讨论】:

这个赋值似乎被破坏了,因为它可以写出包含范围的边界。 @Kerrek SB:当然。正如我所写,代码已尽可能简化。实际代码长度超过 1000 行,具有更复杂的切片、相应的迭代器和其他功能。它还检查边界:) 【参考方案1】:

您需要分离类型才能以正确的方式正常工作。由于您发现的非常相似的原因,iteratorconst_iterator 对于所有标准库容器都是不同的类型。

也就是说,我认为这在很大程度上取决于您的用例/代码库和编码风格,我是否会建议沿着这条路线走(因为保护编码人员免受可能永远不会成为问题的事情的开销很大)您的用例)。

如果您想尝试一下,一个解决方案可能如下所示:

namespace detail

    template<class T, bool Const>
    struct SharedInternalsT;

    template<class T>
    struct SharedInternalsT<T, true>
    
        const T * m_data;
        std::size_t m_size;
    ;

    template<class T>
    struct SharedInternalsT<T, false>
    
        T * m_data;
        std::size_t m_size;
    ;

    template<class T>
    using SharedInternals = SharedInternals<T, false>;

    template<class T>
    using ConstSharedInternals = SharedInternals<T, true>;


template<class T, bool Const>
class SharedMemT

public:

    using Traits = SharedMemTraits<T, Const>;
    using Ptr = typename Traits::Ptr;

    //now we can safely copy in a const correct way.
    SharedMemT(const SharedMemT & _other) :
    m_mem(_other.m_mem),
    m_internals(_other.m_internals)
    

    

private:

    std::shared_ptr<std::vector<T>> m_mem;
    detail::SharedInternals<T, Const> m_internals;
;

template<class T>
using SharedMem = SharedMemT<T, false>;

template<class T>
using ConstSharedMem = SharedMemT<T, true>;

这将是解决问题的第一步。您很可能必须引入更多间接,以便能够从非常量版本正确构造 ConstVersions(可能通过使用 std::enable_if 等启用/禁用某些模板化复制构造函数)。正如我所说,如果您正在构建某种符合标准库的代码,我只会走这条路。如果您只是为您的游戏或类似的东西构建一个小型实用程序,请忽略 const 的正确性,不要浪费您的时间。

【讨论】:

感谢您的建议!与 const 迭代器的类比是一个很好的观点,而且确实很有见地。与单个(可能是 const 限定的)迭代器类型相比,对两种迭代器类型的需求对我来说似乎总是次优设计。但我现在更清楚地看到了问题。

以上是关于具有共享所有者语义的容器的主要内容,如果未能解决你的问题,请参考以下文章

volatile语义

泊坞窗容器怎样才能在没有主机操作系统的安装份额完全访问Windows共享?

是否可以在 docker 容器之间共享内存?

volatile写读的内存语义

如何将 python 库从主机共享到多个 docker 容器?

理解AbstractQueuedSynchronizer提供的独占锁和共享锁语义