Как получить доступ к положению объекта в векторе при использовании итератора? - PullRequest
1 голос
/ 09 июля 2020

Я сейчас делаю это:

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

Так как это рекомендуемый способ вывести все элементы вектора. Проблема в том, что это означает, что все мои члены этого вектора выводятся в единственной строке. Я хочу, чтобы вместо этого я выводил 5 в каждой строке, чтобы мне не приходилось слишком долго прокручивать. Если бы я не использовал итератор, поэтому мой i был просто int, я мог бы просто сказать if (i%5 == 0), затем cout << endl, но, поскольку это итератор, я запутался. Я пробовал по-разному сказать, что хочу сделать, но не могу найти ответа ... Возможно ли это с помощью итератора, или мне лучше использовать int ...?

Ответы [ 7 ]

3 голосов
/ 09 июля 2020

Добавить int pos = i-myVec.begin(). Затем вы можете использовать if (pos%5 == 0) по своему усмотрению.

3 голосов
/ 09 июля 2020

Да, вы можете сделать это с помощью итераторов:

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

В этом случае подсчет l oop также будет работать:

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

Вы можете написать это с помощью a также диапазон для l oop:

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";
}
1 голос
/ 09 июля 2020

В 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";
    }
}
0 голосов
/ 10 июля 2020
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), добавляя при необходимости дополнительные разрывы строк в вывод, например:

#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, ", ")
);

Live Demo

0 голосов
/ 09 июля 2020

Просто для удовольствия, вот пример кода, который позволяет вам получить доступ как к индексу, так и к значению через range-for l oop. Это имеет ограниченное значение, но показывает общий подход (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;
 }
0 голосов
/ 09 июля 2020

Как всегда, возможностей для этого много. Но чтобы ответить на ваш первоначальный вопрос:

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

Это печатает значение в позиции итератора и индекс в векторе. Вы вычисляете разницу между begin() вектора и текущей позицией итератора, которая является индексом.

Хотя это возможно, я бы не рекомендовал это, потому что у вас есть дополнительное вычитание в каждую итерацию. Как правило, это не та ситуация, когда итераторы очень полезны, как вы сами убедились.

Если у вас есть доступ к достаточно современному компилятору C ++ (который, я думаю, у вас есть), вы можете использовать очень элегантный циклы for на основе диапазона :

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

Это «извлекает» ссылки на объекты в вашем векторе. Конечно, теперь вы еще дальше от позиции индекса, потому что вы не можете использовать трюк с итератором с вычитанием. Чтобы решить вашу проблему с сохранением индекса, вы можете просто сохранить отдельный счетчик индекса:

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++) ..., хотя это не так ясно, как версия на основе диапазона для-l oop. Используя диапазон для -l oop, можно сразу увидеть, что что-то сделано со всеми объектами в векторе.

0 голосов
/ 09 июля 2020

Вы можете объявить другую переменную, чтобы использовать ее для подсчета:

// 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...