2014-02-11 146 views
2

我有一個大的std::vector<int> a,但我只想在它的一個子集上工作。這個想法是創建一個std::vector<reference_wrapper<int> > refa,其中只包含所述的子集(在MSE中,所有元素都是1 < a < 4)。然後,我想通過refa函數,期望std::vector<int>std::vector<int>&作爲參數(因爲我也想用它們與a)。 a可能非常大,我想避免多次選擇。傳遞矢量<reference_wrapper <int>>到矢量<int>?

問題

如何正確傳遞refa的功能呢?我想要的是bar(refa)foobar(refa)工作。

有沒有更好的方法來解決問題,而不改變功能(太多)?

代碼

#include <functional> 
#include <iostream> 
#include <vector> 


int foo(int &a) 
{ 
    a++; 
    return 0; 
} 

int bar(std::vector<int> &va) 
{ 
    for(auto &vaa : va) 
    vaa++; 

    return 0; 
} 

int foobar(std::vector<int> va) 
{ 
    for(auto &vaa : va) 
    vaa++; 

    return 0; 
} 



int main() 
{ 
    std::vector<int> a= {1, 2, 3, 4, 5}; 
    std::vector<std::reference_wrapper<int> > refa; 

    //Fill refa 
    for(auto &aa : a) 
    { 
     if(aa>1 && aa<4) refa.push_back(std::ref(aa)); 
    } 


    //works 
    // for(auto &aa : refa) 
    // aa++; 

    //works 
    // bar(a); 

    //works, a unchanged 
    // foobar(a); 

    //works 
    // for(auto &aa : refa) 
    // foo(aa); 

    //works       
    // for(int &aa : refa) 
    // foo(aa) 

    // does not work 
    // conversion from vector<reference_wrapper<int> > to vector<int>& or vector<int> required 
    bar(refa); 
    // foobar(refa); 


    for(auto &aa : a) 
    std::cout << aa << std::endl; 


    return 0; 
} 

注意 int在這裏僅用來保持例子簡單。

+1

也許獲得一個迭代器範圍?作爲「觀點」? –

+1

其中1 piokuc

+0

是的,它們可能不在連續範圍內,我不想對它們進行分類。 – Jost

回答

5

我肯定會使用迭代特別是考慮你的問題提前(在載體上的一個子集工作):

template<class Iterator> 
int bar(Iterator begin, Iterator end) 
{ 
    for (auto it = begin; it != end; ++it) 
     (*it)++; 
    return 0; 
} 

所以你不僅抽象遠離容器,但你也可以很容易地通過從經典的「開始」和「結束」迭代不同的迭代器,以模擬特定範圍

bar(a.begin() + 2, a.begin() + 4); 

例如,與上面的代碼你將訪問的元素從1到4(均被排除)。而here就是這個活生生的例子。

+0

但是這隻能在連續的範圍內工作,不是嗎?在我的例子中的確如此,但在實際應用中並不一定如此。 – Jost

+0

@Jost,這將適用於每個符合['Iterator'](http://en.cppreference.com/w/cpp/concept/Iterator)概念的迭代器。要執行'x.begin()+ y',是的,您需要滿足['RandomAccessIterator'](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator)概念。 – Shoe

+1

@Jost不是真的;用一個合適的迭代器適配器(如[boost :: filter_iterator'](http://www.boost.org/doc/libs/1_54_0/libs/iterator/doc/filter_iterator.html)),天空就是極限。 – Angew

3

最好的辦法是使功能欄和foobar的模板函數,就像這樣:

template <typename TContainer> 
int bar(TContainer& va) 
{ 
    for(auto& vaa : va) 
     vaa++; 

    return 0; 
} 

沒有重新定義你的函數接受「看起來像載體類型」,我不認爲有一個實現你想要的方式。

+2

另一種方法是接受一對迭代器並使用例如['boost :: filter_iterator'](http://www.boost.org/doc/libs/1_54_0/libs/iterator/doc/filter_iterator.html),但它仍然需要使用模板。 – Angew

1

如果您不需要容器,請勿使用容器。使用可迭代的範圍。

容器既是可迭代的範圍,也是其內容的所有者。

vector的情況下,它是一個連續的可迭代範圍和內容的所有者。機率是你只需要知道在你的實現代碼中是否是一個隨機訪問迭代範圍。

但是,處理任意連續的可迭代範圍會產生成本,因爲您必須將實現放在模板頭文件中,或者執行昂貴的類型擦除。解決這個問題的兩種方法是使用這樣一個事實,即您只接受vector的子範圍,或者使用僅接受連續範圍的子範圍的事實。

我喜歡連續範圍的想法自己:

template<typename T> 
struct contiguous_range { 
    T* b; T* e; 
    contiguous_range(contiguous_range const&) = default; 
    contiguous_range():b(nullptr), e(nullptr) {}; 
    contiguous_range& operator=(contiguous_range const&) = default; 
    std::size_t size() const { return e-b; } 
    T* begin() const { return b; } // note, T* 
    T* end() const { return e; } // note, T* 
    template<typename U, typename=typename std::enable_if< 
    sizeof(U)==sizeof(T) 
    && std::is_convertible<U*, T*>::value 
    >::type> 
    contiguous_range(contiguous_range<U> const& o):b(o.b), e(o.e) {}; 
    T& operator[](std::size_t i) const { return b[i]; } 

    template<typename A> 
    contiguous_range(std::vector<T, A> const& v):b(v.data()), e(v.data()+v.size()) {} 
    template<typename U, std::size_t N, typename=typename std::enable_if< 
    sizeof(U)==sizeof(T) 
    && std::is_convertible<U*, T*>::value 
    >::type> 
    contiguous_range(std::array<U, N> const& a):b(a.data()), e(a.data()+a.size()) {} 
    template<typename U, std::size_t N, typename=typename std::enable_if< 
    sizeof(U)==sizeof(T) 
    && std::is_convertible<U*, T*>::value 
    >::type> 
    contiguous_range(U(&a)[N]):b(&a[0]), e((&a[0])+N) {} 

    template<typename U, typename=typename std::enable_if< 
    sizeof(U)==sizeof(T) 
    && std::is_convertible<U*, T*>::value 
    >::type> 
    contiguous_range(U* b_, U* e_):b(b_), e(e_) {} 
}; 

template<typename I> 
auto contiguous_subrange(I b, I e) 
-> contiguous_range<std::iterator_traits<I>::value_type> 
{ 
    return {&*b, &*e}; 
} 
template<typename C> 
auto contiguous_subrange(C&& c, std::size_t start, std::size_t end) 
-> decltype(contiguous_subrange(&c[start], &c[end])) 
    { return (contiguous_subrange(&c[start], &c[end])) }; 

現在,我們的功能可以簡單地採取contiguous_range<int>continguos_range<const int>,並且它們可以被隱式傳遞一個std::vector<int>

您也可以設置std::vector的子範圍,它們是同樣連續的。

請注意,constiguous_range<int>對應於std::vector<int>&,而contiguous_range<const int>對應於std::vector<int> const&