简体   繁体   English

使用迭代器时,如何在向量内访问 object 的 position?

[英]How to access the position of an object within a vector when using an iterator?

I'm currently doing this:我目前正在这样做:

for (std::vector<myClass>::const_iterator i = myVec.begin(); i != myVec.end(); ++i) {
    std::cout << *i << ", ";
}

As it is the recommended way to output all the members of a vector.由于它是 output 的所有成员向量的推荐方式。 The problem is that this means that all of my members of this vector get output on a singular line.问题是这意味着我这个向量的所有成员都在一条奇异线上得到 output。 I want to have it so that I instead output 5 on each line, so that I don't have to scroll too long.我想拥有它,以便在每行上改为 output 5,这样我就不必滚动太久。 If I didn't use an iterator, so my i was just an int , I could just say if (i%5 == 0) then cout << endl but because it is an iterator, I am confused.如果我没有使用迭代器,所以我的i只是一个int ,我可以说if (i%5 == 0) then cout << endl但因为它是一个迭代器,我很困惑。 I tried varying ways of saying what I want to do, but I can't find an answer... Is this possible with an iterator, or should I just use an int instead..?我尝试了不同的方式来表达我想要做的事情,但我找不到答案......这是否可以使用迭代器,或者我应该只使用 int 来代替......?

Yes, you can do this with iterators:是的,你可以用迭代器做到这一点:

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";
}

In this case, a counting loop would work as well:在这种情况下,计数循环也可以工作:

for (auto i = 0u; i < myVec.size(); ++i) {
    std::cout << myVec[i] << ", ";
    if (i && i % 5 == 0)
        std::cout << "\n";
}

You could write this with a range-for loop as well:你也可以用 range-for 循环来写这个:

int i = 0;
for (auto &elem : myVec) {
    std::cout << elem << ", ";
    if (++i % 5 == 0)
        std::cout << "\n";
}

If you use the range-v3 library, you could do:如果你使用 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";
}

or instead:或者改为:

namespace rv = ranges::views;
for (auto [i, line] : rv::enumerate(myVec)) {
    std::cout << elem << ", ";
    if (i && i % 5 == 0)
        std::cout << "\n";
}

Add int pos = i-myVec.begin() .添加int pos = i-myVec.begin() Then you can use if (pos%5 == 0) as you wanted.然后您可以根据需要使用if (pos%5 == 0)

In C++20, you might do:在 C++20 中,您可能会这样做:

for (int counter = 0; auto&& e : myVec) {
    std::cout << e << ", ";
    if (++counter == 5) {
        counter = 0;
        std::cout << "\n";
    }
}

Before,前,

int counter = 0;
for (auto&& e : myVec) {
    std::cout << e << ", ";
    if (++counter == 5) {
        counter = 0;
        std::cout << "\n";
    }
}

You can declare a different variable to use it for counting:您可以声明一个不同的变量以将其用于计数:

// 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 << ", ";
}

You'll then get:然后你会得到:

1, 2, 3, 4, 5,        // 5 values
6, 7, 8, 9, 10,       // 5 values
11, 12, 13, 14, 15,   // 5 values

Note: You may use auto keyword to reduce such long declaration:注意:您可以使用auto关键字来减少这样长的声明:

std::vector<myClass>::const_iterator i = myVec.begin();
// better:
auto i = myVec.begin();

As always, there are a lot of possibilities to achieve this.与往常一样,实现这一目标有很多可能性。 But to answer your initial question:但要回答你最初的问题:

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";
    }
}

This prints the value at the iterator position and the index in the vector.这将打印迭代器 position 处的值和向量中的索引。 You calculate the difference between the begin() of the vector and the current iterator position, which is the index.您计算向量的begin()与当前迭代器 position 之间的差,即索引。

While it is possible to do this, I wouldn't recommend it because you have an additional subtraction in every iteration.虽然可以这样做,但我不建议这样做,因为您在每次迭代中都有额外的减法。 This is generally not a situation where iterators are very useful, as you've seen for yourself.正如您亲眼所见,这通常不是迭代器非常有用的情况。

If you have access to a sufficiently modern C++ compiler (which I guess you do), you can use the very elegant range-based for-loops :如果您可以访问足够现代的 C++ 编译器(我猜您会这样做),您可以使用非常优雅的基于范围的 for 循环

for (myClass& obj : myVec) {
    std::cout << obj;
}

This "extracts" references to the objects in your vector.这会“提取”对向量中对象的引用。 Of course, now you're even farther away from the index position because you can't use the iterator-subtraction trick.当然,现在您离索引 position 更远了,因为您不能使用迭代器减法技巧。 To solve your problem of keeping an index you can simply keep a separate index counter:要解决保留索引的问题,您可以简单地保留一个单独的索引计数器:

int i = 0;
for (myClass& obj : myVec) {
    std::cout << obj << ", ";
    if (i % 5 == 0) {
        std::cout << "\n";
    }
    i++;
}

Or even use the very new C++20 feature of inline-initialized variables (I don't remember the official name for these):或者甚至使用内联初始化变量的 C++20 新特性(我不记得这些的正式名称):

for (int i = 0; myClass& obj : myVec) {
    std::cout << obj << ", ";
    if (i++ % 5 == 0) {
        std::cout << "\n";
    }
}

Or you can just use the classical for (int i = 0; i < size; i++)... , though this isn't as clear as the range-based for-loop version.或者您可以只使用经典for (int i = 0; i < size; i++)... ,尽管这不像基于范围的 for 循环版本那样清晰。 With the range-based for-loop it's possible to instantly see that something is done with all objects in the vector.使用基于范围的 for 循环,可以立即看到向量中的所有对象都完成了某些操作。

Just for the fun of it, here is a sample code which allows you to access to both index and value through range-for loop.只是为了好玩,这里有一个示例代码,它允许您通过 range-for 循环访问索引和值。 This has limited value, but shows general approach (RangesTS has this built-in):这具有有限的价值,但显示了一般的方法(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);
}

Alternatively, you could write a custom iterator that mimics std::ostream_iterator (or better, infix_iterator ), injecting additional line breaks into the output when needed, eg:或者,您可以编写一个模仿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;
    }
};     

Then you can use it like this:然后你可以像这样使用它:

my_infix_ostream_iterator<myClass> iter(std::cout, 5, ", ");
for (const auto &elem : myVec) {
    *iter++ = elem;
}

or:或者:

std::copy(myVec.begin(), myVec.end(),
    my_infix_ostream_iterator<myClass>(std::cout, 5, ", ")
);

Live Demo现场演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM