2014-12-30 102 views
1

在C#中,我可以做以下提升模擬C#的IEnumerable的方式?

class MyClass { 
    private List<int> lst; 
    public IEnumerable<int> Numbers { get { return lst; } } 
} 

這有給予lst公衆只讀訪問,而List成員變量暴露給公衆的影響。例如,該課程的用戶可以編寫

foreach (var n in myClass.Numbers) { /* do something with the values */}; 

但他們不能改變列表或以相反的順序遍歷它。這使我可以在不破壞使用類的代碼的情況下更改實現。

我想要做類似的東西在C++:

class MyClass { 
    std::vector<int> lst; 
public: 
    /* ???? */ getNumbers() const { return /* ??? */ lst; } 
}; 

// I want to be able to write the following 
auto rng = myClass.getNumbers(); 
auto b = begin (rng); 
auto e = end (rng); 
// now b and e should be readonly (!) forward (!) iterators 

我不關心編譯時的依賴,即我不想用any_range。然而我想 希望在我的代碼中明確表示我只保證只讀轉發行爲。

+0

對於那些我們不熟悉C#的人,請描述一下'IEnumerable'是什麼,以及你的C#示例是幹什麼的。 –

+3

沒有並行顯式數據結構。 C++使用帶編譯時多態的迭代器(模板)來獲得類似的功能。因此,只需返回'const std :: vector &'和std :: begin和std :: end應該按預期工作。 – IdeaHat

+0

實際上,JohnB可以只返回一個迭代器範圍 - 也就是說,一對迭代器給出開始和結束,或者更好,帶有前向迭代器的boost :: iterator_range - 從而隱藏底層容器實現。 –

回答

2

你可以得到一個迭代器類型

template <typename Base> 
struct const_forward_iterator : public Base { 
    using base_type   = Base; 
    using iterator_type  = const_forward_iterator; 
    using iterator_category = std::forward_iterator_tag; 

    const_forward_iterator& operator+=(typename Base::difference_type) = delete; 
    const_forward_iterator& operator-=(typename Base::difference_type) = delete; 
    const_forward_iterator operator+ (typename Base::difference_type) = delete; 
    const_forward_iterator operator- (typename Base::difference_type) = delete; 

    const_forward_iterator(base_type it) : base_type(it) {} 
}; 

和底座的簡單範圍關閉它

template <typename it> 
struct range : std::pair<it,it> { 
    range(it b, it e) : std::pair<it,it>(b,e) {} 

    it begin() const { return this->first; } 
    it end() const { return this->second; } 
}; 

及利潤:

using iterator_type = const_forward_iterator<std::vector<int>::const_iterator>; 
range<iterator_type> getNumbers() const { return { lst.cbegin(), lst.cend() }; } 

Live On Coliru

#include <vector> 
#include <iterator> 

namespace detail { 
    template <typename Base> 
    struct const_forward_iterator : public Base { 
     using base_type   = Base; 
     using iterator_type  = const_forward_iterator; 
     using iterator_category = std::forward_iterator_tag; 

     const_forward_iterator& operator+=(typename Base::difference_type) = delete; 
     const_forward_iterator& operator-=(typename Base::difference_type) = delete; 
     const_forward_iterator operator+ (typename Base::difference_type) = delete; 
     const_forward_iterator operator- (typename Base::difference_type) = delete; 

     const_forward_iterator(base_type it) : base_type(it) {} 
    }; 

    template <typename it> 
    struct range : std::pair<it,it> { 
     range(it b, it e) : std::pair<it,it>(b,e) {} 

     it begin() const { return this->first; } 
     it end() const { return this->second; } 
    }; 
} 

class MyClass { 
    std::vector<int> lst { 1, 3, 77, 42 }; 
    public: 

    using iterator_type = detail::const_forward_iterator<std::vector<int>::const_iterator>; 

    detail::range<iterator_type> getNumbers() const { return { lst.cbegin(), lst.cend() }; } 
}; 

#include <iostream> 

int main() 
{ 
    MyClass o; 

    for (auto i : o.getNumbers()) 
     std::cout << i << " "; 

    auto numbers = o.getNumbers(); 
    //numbers.first += 2; error: use of deleted function 
} 

輸出

1 3 77 42 

當然你也可以因素的代碼,因此可以重用位,但是這是打算作爲一個自足的PoC

1

正如我現在發現,boost::range是最接近的C++相當於IEnumerable