#c #c 11 #stl
#c #c 11 #stl
Вопрос:
Итак, я разрабатываю класс, который подключается (по сети) к сервису для получения некоторых данных. Я не знаю, сколько точек данных man я буду получать заранее. Тем не менее, мне было интересно, есть ли способ сделать этот класс итеративным с помощью forward_iterator, чтобы насладиться STL во всей его красе. Моя идея была примерно такой:
self_type operator () {
// if there are some locally cached data points left return next
// else connect to service again to receive the next batch of data
}
Однако, поскольку я не могу предоставить действительный end()
ответ, мне любопытно, возможно ли это каким-то образом все еще сделать.
Альтернативный (и не использующий итераторов) интерфейс, вероятно, выглядел бы примерно так:
bool hasMoreDataPoints() const;
DataPointamp; getNext();
что, очевидно, не будет работать ни с одним STL-алгоритмом.
Комментарии:
1. Похоже, вам нужно реализовать свой итератор, как это делают потоки, и
end
это просто созданный по умолчанию итератор.
Ответ №1:
Поступайте так же, как со стандартной библиотекой istream_iterator
: когда у вас заканчиваются данные, установите состояние итератора таким образом, чтобы оно сравнивалось с созданным по умолчанию объектом этого типа. И тогда есть ваш end()
эквивалент.
Комментарии:
1. Не могли бы вы уточнить? Итак, я бы все равно унаследовал, например, от std::iterator и реализовал интерфейс простым способом, но просто выполнил проверку на равенство внутри
end()
функции?2. @NewProggie не наследует от
std::iterator
, это устаревшая практика в C 17.3. @KABoissonneault — но сейчас только 2016 год. <g> И если ты даешь такой совет, тебе нужно объяснить, что делать вместо этого.
4. @NewProggie Посмотрите, как
istream_iterator
используется, и напишите свой итератор таким же образом.5. Если вы заинтересованы в том, чтобы ваши итераторы были совместимы со стандартными алгоритмами, то либо предоставьте
typedef
сами непосредственно свой класс итератора, либо специализируйтесьstd::iterator_traits
. Вам не нужно предоставлять те, которые вам не нужны
Ответ №2:
Однако, поскольку я не могу предоставить действительный конец (), мне любопытно, возможно ли это каким-то образом….
Вы можете использовать любой из классов контейнеров, поставляемых с STL. Предположим, вы используете вектор, или список, или любой другой подходящий контейнер. Просто используйте то, что предоставляется STL, и напишите свою собственную оболочку
vector<datapoint> mydpvector;
//as data comes in
mydpvector.push_back(newdp);
vector<datapoint>::const_iterator cit=mydpvector.begin();
//now iterate everytime over the container
for(cit;cit!=mydpvector.end();cit )
{
//get the data you need
}
//alternately if you need just the last element do this
mostrecentdp = mydpvector.back();
Ответ №3:
Я полагаю, что в вашем классе есть какой-то тип хранилища, который вы используете для кэширования, например, a std::vector
. Затем вы можете просто выставить std::begin()
std::end()
итераторы its и (во всех обычных формах). Затем алгоритмы напрямую работают с базовым контейнером и используют функции-члены итератора контейнера, такие как operator
, operator==
и так далее.
В случае, если вам нужно ввести больше логики, вам нужно создать свой собственный итератор. Это может быть в основном сделано с помощью composition, т. Е. Написать новый класс, который содержит итератор, соответствующий вашему контейнеру хранения, и предоставить всю необходимую функциональность, соответствующим образом ее настроив.
РЕДАКТИРОВАТЬ: как вы сказали, вы используете список:
struct DataPoints
{
std::list<double> _list;
auto begin() const
{
return _list.begin();
}
auto end() const
{
return _list.end();
}
//other versions: non-const begin and end, cbegin, cend
}
Вот и все уже для простого подхода. Вы можете использовать это просто как обычный список:
DataPoints d;
std::find(d.begin(), d.end(), 10.0);
Как уже было сказано, если вам нужно больше логики, вам, вероятно, нужно написать пользовательский итератор.
Комментарии:
1. Я бы, вероятно, использовал здесь std::list, поскольку я хочу избежать аннулирования итератора при изменении базового контейнера. Как бы мне тогда адаптировать реализацию итератора std::lists?
2. Это зависит от того, что вы хотите. Если вы просто хотите перебирать свои точки данных или применять стандартные библиотечные алгоритмы, все в порядке. Если вам нужна дополнительная логика в компонентах внутреннего итератора, которые расширяют функциональность итератора списков, вышесказанного недостаточно. Например, если вы хотите, чтобы вы
operator
случайным образом перебирали данные. Для таких вещей вам нужно написать свой собственный итератор.3. Кстати: нет необходимости ограничивать реализацию прямым итератором (как указано в названии вопроса). В общем, он должен использовать ту же категорию итератора, что и у базового контейнера.