2013-10-27 141 views
9

我試圖定義我的軟件,這意味着並注意一些變量讀/寫訪問一個好的設計。我在這裏簡化了討論的計劃。希望這對其他人也有幫助。 :-)的shared_ptr <T>到shared_ptr <T const>和矢量<T>到矢量<T const>

比方說,我們有一個類X如下:

class X { 
    int x; 
public: 
    X(int y) : x(y) { } 
    void print() const { std::cout << "X::" << x << std::endl; } 
    void foo() { ++x; } 
}; 

我們也可以說,在未來這一類將與X1,X2,被繼承...可以重新實現print()foo() 。 (我省略了需要virtual關鍵字爲簡單起見在這裏,因爲這不是我所面對的實際問題。)

因爲我們將用多態性研究,讓我們使用(智能)指針和定義一個簡單的工廠:

using XPtr = std::shared_ptr<X>; 
using ConstXPtr = std::shared_ptr<X const>; 

XPtr createX(int x) { return std::make_shared<X>(x); } 

到現在爲止,一切都很好:我可以定義goo(p),它可以讀取和寫入phoo(p),它只能讀取p

void goo(XPtr p) { 
    p->print(); 
    p->foo(); 
    p->print(); 
} 

void hoo(ConstXPtr p) { 
    p->print(); 
// p->foo(); // ERROR :-) 
} 

而調用點看起來像這樣:

XPtr p = createX(42); 

    goo(p); 
    hoo(p); 

共享指針X(XPtr)被自動地轉換到其const版本(ConstXPtr。很好,這正是我想要的!

現在來麻煩了:我需要一個異構集合X。我的選擇是std::vector<XPtr>。 (它也可能是list,爲什麼不)。

我想到的設計是以下幾點。我有兩個版本的容器:一個對其元素具有讀/寫訪問權限,另一個對其元素進行只讀訪問。

using XsPtr = std::vector<XPtr>; 
using ConstXsPtr = std::vector<ConstXPtr>; 

我有處理這個數據的類:

class E { 
    XsPtr xs; 
public: 
    E() { 
     for (auto i : { 2, 3, 5, 7, 11, 13 }) { 
      xs.emplace_back(createX(std::move(i))); 
     } 
    } 

    void loo() { 
     std::cout << "\n\nloo()" << std::endl; 
     ioo(toConst(xs)); 

     joo(xs); 

     ioo(toConst(xs)); 
    } 

    void moo() const { 
     std::cout << "\n\nmoo()" << std::endl; 
     ioo(toConst(xs)); 

     joo(xs); // Should not be allowed 

     ioo(toConst(xs)); 
    } 
}; 

ioo()joo()功能如下:

void ioo(ConstXsPtr xs) { 
    for (auto p : xs) { 
     p->print(); 
//  p->foo(); // ERROR :-) 
    } 
} 

void joo(XsPtr xs) { 
    for (auto p: xs) { 
     p->foo(); 
    } 
} 

正如你所看到的,在E::loo()E::moo()我必須做一些轉換toConst()

ConstXsPtr toConst(XsPtr xs) { 
    ConstXsPtr cxs(xs.size()); 
    std::copy(std::begin(xs), std::end(xs), std::begin(cxs)); 
    return cxs; 
} 

但是,這意味着遍地....複製的一切: -/

此外,在moo(),這是常量,我可以叫joo()將修改xs的數據。不是我想要的。在這裏,我寧願編譯錯誤。

完整的代碼在ideone.com

問題是:是否有可能做到這一點,但沒有將矢量複製到它的const版本?或者更一般地說,是否有一種既高效又易於理解的良好技術/模式?

謝謝。 :-)

+0

獲取'boost :: adapters :: transformed'的'const'-view和一個appropri吃了功能對象來轉換你的共享指針。 – Xeo

+0

@Xeo:我很快看了'boost :: adapters :: transformed',但是似乎我必須在某些時候複製東西,有點像上面的但有不同的語法,對吧?如果情況並非如此,您是否介意在下面舉個例子? :-) – Hiura

+0

只是一個說明。你的'std :: move(i)'不會移動任何東西。 「移動」不移動,它只是一個演員。也許這只是從你的實際代碼複製到它的位置:) – typ1232

回答

0

根據評論和答案,我最終創建了容器視圖。

基本上我定義了新的迭代器。我在github上創建一個項目:mantognini/ContainerView

的代碼可能可以改善,但其主要思想是具有兩個模板類,ViewConstView,具有用於迭代底層容器上的begin()end()方法現有容器(例如std::vector<T>)上。

通過一點點繼承(ViewConstView),它有助於在需要時將讀寫轉換爲只讀視圖,而無需額外的代碼。

由於我不喜歡指針,因此我使用模板特化來隱藏std::shared_ptr:在std::shared_ptr<T>的容器上的視圖不需要額外的解引用。 (我還沒有實現它的原始指針,因爲我沒有使用它們。)

Here is a basic example of my views in action.

6

我認爲通常的回答是,對於一個類模板X<T>,任何X<const T>可以專業,因此編譯器不允許簡單地假設它可以將指針或X<T>基準轉換爲X<const T>,並且有不是一般的表達方式,這兩者實際上是可轉換的。但是,然後我雖然:等等,有一種方式可以說X<T>IS AX<const T>IS A通過繼承來表達。

儘管這對std::shared_ptr或標準容器沒有幫助,但在實現自己的類時可能需要使用這種技術。事實上,我想知道std::shared_ptr和集裝箱是否可以/應該改進以支持這一點。任何人都可以看到這個問題嗎?

我心目中的技術將這樣的工作:

template< typename T > struct my_ptr : my_ptr< const T > 
{ 
    using my_ptr< const T >::my_ptr; 
    T& operator*() const { return *this->p_; } 
}; 

template< typename T > struct my_ptr< const T > 
{ 
protected: 
    T* p_; 

public: 
    explicit my_ptr(T* p) 
     : p_(p) 
    { 
    } 

    // just to test nothing is copied 
    my_ptr(const my_ptr& p) = delete; 

    ~my_ptr() 
    { 
     delete p_; 
    } 

    const T& operator*() const { return *p_; } 
}; 

Live example

+0

你是否試圖編譯它? – 2013-10-27 15:09:09

+0

@typical是的,請參閱答案中的「Live example」鏈接,隨時隨地使用它。 –

+1

'的std :: shared_ptr的'可以通過*轉換爲它的const版本*'模板< class Y > shared_ptr的(常量的shared_ptr & r);'。這就是我的問題的第一部分以'咕發生()'和'HOO( )''所以我想我不必爲共享指針類重新創建輪子,對吧? – Hiura

1

有一個根本性的問題與你想要做什麼。

一個std::vector<T const*>不是std::vector<T*>的限制,而同樣是含有智能指針及其const版本vector真的。

具體而言,我可以在第一個容器中存儲指向const int foo = 7;的指針,但不是第二個。 std::vector既是範圍又是容器。它類似於T** vs T const**問題。

現在,在技術上std::vector<T const*> conststd::vector<T>的限制,但不支持。

解決此問題的一種方法是啓動workimg eith範圍視圖:非擁有其他容器的視圖。一個非擁有T const*迭代器視圖到std::vector<T *>是可能的,並且可以給你你想要的接口。

boost::range可以爲你做樣板,但是自己寫contiguous_range_view<T>random_range_view<RandomAccessIterator>並不難。當你想要自動檢測迭代器類別並啓用基於此的功能時,它會變得很花哨,這就是爲什麼boost::range包含更多代碼。

+0

你能詳細介紹一下這些boost :: range嗎?也許通過展示如何使用ioo和joo來使用這些以及調用網站會是什麼樣子?謝謝。 – Hiura

+0

如果我正確讀取boost文檔,'std :: vector '替換爲'boost :: iterator_range :: const_iterator>'。 – Yakk

1

Hiura,

我試圖從回購和g ++ 4.8編譯代碼返回了一些錯誤。 main.cpp中的更改:97,其餘行以lambda函數調用view :: create()作爲第二個參數。 +加+

auto f_lambda([](view::ConstRef_t<view::ElementType_t<Element>> const& e) { return ((e.getX() % 2) == 0); }); 

std::function<bool(view::ConstRef_t<view::ElementType_t<Element>>)> f(std::cref(f_lambda)); 

+ MOD +

printDocument(view::create(xs, f)); 

也View.hpp:185需要額外的操作,即: +加+

bool operator==(IteratorBase const& a, IteratorBase const& b) 
{ 
    return a.self == b.self; 
} 

BR, 馬立克Szews

+0

感謝您的支持,但您應該在Github上打開一個問題,而不是在這裏回答 - 如果您想遵循SO的精神。 – Hiura

相關問題