[英]How to access the position of an object within a vector when using an iterator?
我目前正在這樣做:
for (std::vector<myClass>::const_iterator i = myVec.begin(); i != myVec.end(); ++i) {
std::cout << *i << ", ";
}
由於它是 output 的所有成員向量的推薦方式。 問題是這意味着我這個向量的所有成員都在一條奇異線上得到 output。 我想擁有它,以便在每行上改為 output 5,這樣我就不必滾動太久。 如果我沒有使用迭代器,所以我的i
只是一個int
,我可以說if (i%5 == 0)
then cout << endl
但因為它是一個迭代器,我很困惑。 我嘗試了不同的方式來表達我想要做的事情,但我找不到答案......這是否可以使用迭代器,或者我應該只使用 int 來代替......?
是的,你可以用迭代器做到這一點:
for (std::vector<myClass>::const_iterator i = myVec.begin(); i != myVec.end(); ++i) {
std::cout << *i << ", ";
if (i != myVec.begin() && std::distance(myVec.begin(), i) % 5 == 0)
std::cout << "\n";
}
在這種情況下,計數循環也可以工作:
for (auto i = 0u; i < myVec.size(); ++i) {
std::cout << myVec[i] << ", ";
if (i && i % 5 == 0)
std::cout << "\n";
}
你也可以用 range-for 循環來寫這個:
int i = 0;
for (auto &elem : myVec) {
std::cout << elem << ", ";
if (++i % 5 == 0)
std::cout << "\n";
}
如果你使用 range-v3 庫,你可以這樣做:
namespace rs = ranges;
for (auto line : myVec | rs::views::chunk(5)) {
rs::copy(line, rs::ostream_iterator<myClass>(std::cout, ",");
std::cout << "\n";
}
或者改為:
namespace rv = ranges::views;
for (auto [i, line] : rv::enumerate(myVec)) {
std::cout << elem << ", ";
if (i && i % 5 == 0)
std::cout << "\n";
}
添加int pos = i-myVec.begin()
。 然后您可以根據需要使用if (pos%5 == 0)
。
在 C++20 中,您可能會這樣做:
for (int counter = 0; auto&& e : myVec) {
std::cout << e << ", ";
if (++counter == 5) {
counter = 0;
std::cout << "\n";
}
}
前,
int counter = 0;
for (auto&& e : myVec) {
std::cout << e << ", ";
if (++counter == 5) {
counter = 0;
std::cout << "\n";
}
}
您可以聲明一個不同的變量以將其用於計數:
// example vector
vector<int> myVec {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
int c = 0;
for (auto i = myVec.begin(); i != myVec.end(); i++) {
// incrementing the counter and verifying if it gives 0 as the remainder
if (c++ % 5 == 0)
cout << endl;
std::cout << *i << ", ";
}
然后你會得到:
1, 2, 3, 4, 5, // 5 values
6, 7, 8, 9, 10, // 5 values
11, 12, 13, 14, 15, // 5 values
注意:您可以使用auto
關鍵字來減少這樣長的聲明:
std::vector<myClass>::const_iterator i = myVec.begin();
// better:
auto i = myVec.begin();
與往常一樣,實現這一目標有很多可能性。 但要回答你最初的問題:
for (std::vector<myClass>::const_iterator i = myVec.begin(); i != myVec.end(); ++i) {
std::cout << *i << ", ";
size_t index = i - myVec.begin();
if (index % 5 == 0) {
std::cout << "\n";
}
}
這將打印迭代器 position 處的值和向量中的索引。 您計算向量的begin()
與當前迭代器 position 之間的差,即索引。
雖然可以這樣做,但我不建議這樣做,因為您在每次迭代中都有額外的減法。 正如您親眼所見,這通常不是迭代器非常有用的情況。
如果您可以訪問足夠現代的 C++ 編譯器(我猜您會這樣做),您可以使用非常優雅的基於范圍的 for 循環:
for (myClass& obj : myVec) {
std::cout << obj;
}
這會“提取”對向量中對象的引用。 當然,現在您離索引 position 更遠了,因為您不能使用迭代器減法技巧。 要解決保留索引的問題,您可以簡單地保留一個單獨的索引計數器:
int i = 0;
for (myClass& obj : myVec) {
std::cout << obj << ", ";
if (i % 5 == 0) {
std::cout << "\n";
}
i++;
}
或者甚至使用內聯初始化變量的 C++20 新特性(我不記得這些的正式名稱):
for (int i = 0; myClass& obj : myVec) {
std::cout << obj << ", ";
if (i++ % 5 == 0) {
std::cout << "\n";
}
}
或者您可以只使用經典for (int i = 0; i < size; i++)...
,盡管這不像基於范圍的 for 循環版本那樣清晰。 使用基於范圍的 for 循環,可以立即看到向量中的所有對象都完成了某些操作。
只是為了好玩,這里有一個示例代碼,它允許您通過 range-for 循環訪問索引和值。 這具有有限的價值,但顯示了一般的方法(RangesTS 有這個內置的):
#include <vector>
#include <utility>
#include <iostream>
template<class UnderlyingIterator>
struct IndexingIterator {
using ValueType = std::pair<long long int, const typename UnderlyingIterator::value_type&>;
IndexingIterator(const UnderlyingIterator it) : underlyingIterator(it) { }
IndexingIterator& operator++() {
++underlyingIterator;
++ix;
return *this;
}
bool operator != (const IndexingIterator& rhs) const {
return underlyingIterator != rhs.underlyingIterator;
}
ValueType operator*() const {
return ValueType{ix, *underlyingIterator};
}
long long int index() const { return ix; }
private:
UnderlyingIterator underlyingIterator;
long long int ix = 0;
};
template<class Container>
struct Enumeratable {
using Iterator = typename Container::const_iterator;
using Indexing = IndexingIterator<Iterator>;
Enumeratable(const Container& container) : b{container.begin()}, e{container.end()} {}
Indexing begin() const { return b; }
Indexing end() const { return e; }
private:
Indexing b;
Indexing e;
};
template<class Container>
auto enumerate(const Container& container) {
return Enumeratable<Container>{container};
}
void testDriver(const std::vector<int>& vec) {
for (const auto& [index, value]: enumerate(vec)) {
std::cout << index << ": " << value << "\n";
}
}
int main() {
std::vector<int> vec{10, 34, 122, 12};
testDriver(vec);
return 0;
}
if (!myVec.empty())
{
auto iter = myVec.begin(), end = myVec.end();
size_t count = 0;
do {
std::cout << *iter++;
if (iter == end) break;
std::cout << ", ";
if ((++count % 5) == 0)
std::cout << '\n';
}
while (true);
}
或者,您可以編寫一個模仿std::ostream_iterator
(或者更好的是infix_iterator
)的自定義迭代器,在需要時向 output 注入額外的換行符,例如:
#include <ostream>
#include <iterator>
template <class T,
class charT = char,
class traits = std::char_traits<charT> >
class my_infix_ostream_iterator :
public std::iterator<std::output_iterator_tag, void, void, void, void>
{
std::basic_ostream<charT, traits> *os;
charT const* delimiter;
size_t counter, numPerLine;
public:
typedef charT char_type;
typedef traits traits_type;
typedef std::basic_ostream<charT,traits> ostream_type;
my_infix_ostream_iterator(ostream_type& s, size_t numPerLine = 0, charT const *d = 0)
: os(&s), delimiter(d), numPerLine(numPerLine), counter(0)
{}
my_infix_ostream_iterator& operator=(T const &item)
{
if (counter > 0)
{
if (delimiter)
*os << delimiter;
if ((numPerLine > 0) && ((counter % numPerLine) == 0))
*os << os->widen('\n');
}
*os << item;
++counter;
return *this;
}
my_infix_ostream_iterator& operator*() {
return *this;
}
my_infix_ostream_iterator& operator++() {
return *this;
}
my_infix_ostream_iterator& operator++(int) {
return *this;
}
};
然后你可以像這樣使用它:
my_infix_ostream_iterator<myClass> iter(std::cout, 5, ", ");
for (const auto &elem : myVec) {
*iter++ = elem;
}
或者:
std::copy(myVec.begin(), myVec.end(),
my_infix_ostream_iterator<myClass>(std::cout, 5, ", ")
);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.