2017-02-25 63 views
0

我正在設計具有共享所有權語義的容器類型。它支持切片,並因此切片共享所有權。我的一個問題是數據共享似乎會干擾常量的正確性,所以我試圖關注這一點,但是我對結果並不滿意。具有共享所有者語義的容器

下面是一個大大細分版本的我的實際代碼:

#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做正確的事情(實際上非​​常不同的事情)。

我不確定我是否遇到麻煩。問題:

  • 這個設計好嗎,還是會引發問題?如果是這樣,哪個?
  • 如果存在嚴重缺陷,如何解決/避免它?

感謝您的任何提示。

+0

賦值似乎中斷,因爲它可以寫出包含範圍的界限。 –

+0

@Kerrek SB:當然。正如我寫的,代碼儘可能簡化。實際的代碼長度大於1000行,其中更復雜的切片,相應的迭代器和其他功能。它也檢查界限:) – tglas

回答

2

您需要分離類型以正確地使用const的方式進行這項工作。對於您發現的非常類似的原因,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>; 

這將是邁向解決方案的第一步。爲了能夠從非const版本(可能通過使用std::enable_if等啓用/禁用某些模板化的拷貝構造函數)正確地構造ConstVersions,您很可能必須引入更多的間接方法。正如我所說的,如果您正在構建某種符合標準庫的代碼,我只會沿着這條路線走下去。如果你只是爲你的遊戲建立一個小工具或者這些工具,那麼忽略const的正確性並且不要浪費你的時間。

+0

感謝您的建議!對常量迭代器的類比是非常好的一點,而且確實很有洞察力。與單個(可能是const限定的)迭代器類型相比,非常需要兩種迭代器類型,這對我來說始終是次優設計。但我現在更清楚地看到這個問題。 – tglas