我已經定義指針的雙端隊列是這樣的:我可以從基於循環的範圍內獲取項目的索引嗎?
std::deque<BoardSquare *> mydeque;
我想使用基於與我deque的循環範圍:
for (BoardSquare * b : mydeque) {
// do something involving the index of b
}
是否有可能得到一個項目從索引在基於循環的範圍內?
我已經定義指針的雙端隊列是這樣的:我可以從基於循環的範圍內獲取項目的索引嗎?
std::deque<BoardSquare *> mydeque;
我想使用基於與我deque的循環範圍:
for (BoardSquare * b : mydeque) {
// do something involving the index of b
}
是否有可能得到一個項目從索引在基於循環的範圍內?
我想出了一個解決方案—或更確切地說是一個實驗的解決方案。這裏是你將如何最終使用它:
for(auto item : make_indexable(v))
{
//std::get<0>(item) is the index
//std::get<1>(item) is the object
}
這裏那張最小實現(它只是爲了演示的基本概念):
#include <tuple>
#include <functional>
template<typename C>
struct indexed_container
{
struct indexed_iterator
{
typedef typename C::value_type value_type;
typedef std::tuple<size_t, std::reference_wrapper<value_type>> tuple_type;
typename C::iterator _it;
size_t _index;
indexed_iterator(typename C::iterator it) : _it(it), _index(0) {}
indexed_iterator& operator++()
{
++_it;
++_index;
return *this;
}
bool operator == (indexed_iterator const & other)
{
return _it == other._it;
}
bool operator != (indexed_iterator const & other)
{
return _it != other._it;
}
tuple_type operator*()
{
return std::make_tuple(_index, std::ref(*_it));
}
};
indexed_container(C & c) : _c(c) {}
indexed_iterator begin()
{
return indexed_iterator(_c.begin());
}
indexed_iterator end()
{
return indexed_iterator(_c.end());
}
private:
C & _c;
};
template<typename C>
auto make_indexable(C & c) -> indexed_container<C>
{
return indexed_container<C>(c);
}
測試代碼:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v{1,2,3};
for(auto item : make_indexable(v))
{
std::cout << std::get<0>(item) << " => " << std::get<1>(item) << std::endl;
std::get<1>(item) *= 10; //modify value!
}
std::cout << "\nModified\n";
for(auto item : make_indexable(v))
{
std::cout << std::get<0>(item) << " => " << std::get<1>(item) << std::endl;
}
}
輸出:
0 => 1
1 => 2
2 => 3
Modified
0 => 10
1 => 20
2 => 30
注意,該方案並不完美,因爲它不會與臨時變量和常量容器(和常量對象的容器)工作。另外,現在的基本對象將即使你寫auto item
,而不是auto & item
(事實上,你可以不寫auto &item
)返回參考。但我認爲這些問題可以通過更多的努力和謹慎的設計來解決。畢竟,這只是對基本想法的展示。
不錯,它讓我想起從'python'枚舉':) – juanchopanza
不,這不是(至少不是以合理的方式)。當你真正需要的指數,那麼你可能不應該使用循環在所有基於範圍的,而是一個良好的老指數型iterator-或環:
// non-idiomatic index-iteration, random access containers only
for(std::size_t i=0; i<mydeque.size(); ++i)
mydeque[i];
// awfully ugly additional iteration variable, yet generic and fast
std::size_t i = 0;
for(auto iter=mydeque.begin(); iter!=mydeque.end(); ++iter,++i)
*iter;
// idiomatic and generic, yet slow for non-random access containers
for(auto iter=mydeque.begin(); iter!=mydeque.end(); ++iter)
{
auto i = std::distance(mydeque.begin(), iter);
*iter;
}
還有所有那些有自己的優勢,以及關於清晰度,慣用性,簡化性和性能的缺點。
看到我的答案是一個骯髒的方式來獲取範圍for循環索引。 – TemplateRex
你需要添加一個額外的變量來跟蹤指數,複製由基於範圍的循環使用(無法訪問)迭代器。你需要確保它的正確初始化,並增加在每次迭代,是特別小心,如果有人增加continue
語句循環就不會出問題。
作爲傳統的for
循環的迭代變量,使用索引(或迭代器,您可以根據需要計算索引)會更簡單且不易出錯。
看看我的解決方案。我想聽聽你對此的評論。 – Nawaz
除了@ChristianRau提供的優秀答案,它顯示了獲取循環索引的首選方法,有一種方法可以從ranged-for循環獲取索引,但前提是您使用std::vector
,因爲這是隻有保證內存中元素連續性的容器。
#include <deque>
#include <vector>
#include <iostream>
int main()
{
auto v = std::vector<int> { 0, 1, 2, 3 };
auto d = std::vector<int*> { &v[0], &v[1], &v[2], &v[3] }; // NOT: std::deque
for (auto ptr: d)
{
// assumes element contiguity, only guaranteed for std::vector!!
auto const i = std::distance(&d[0], &ptr);
std::cout << *(d[i]) << "\n";
}
}
Live output,會崩潰的std::deque
。
那麼,當通過引用而不是按值來引用元素時,它更容易(但不會骯髒):'for(auto && b:v)auto i = &b - v.data();',而不需要額外的向量。好的,你必須記住要引用,但是在你的版本中,無論如何你都需要迭代一個完全不同的容器(這證明了這個問題是否仍然是基於循環的相同範圍)。仍然有趣的方法。 –
如果您需要索引,也許您不應該使用基於範圍的循環。 – juanchopanza
也許......這是否意味着答案是'不'?或者你的評論意味着我混淆了我的範例嗎? – BeeBand
我想不出一種不可怕的方式來做到這一點。 – juanchopanza