Сторнирование итератора по требованию

У меня есть итератор DataIterator который производит значения по требованию, поэтому оператор разыменования возвращает данные, а не данные&, Я думал, что это нормально, пока не попытался сторнировать DataIterator данных, поместив его в reverse_iterator.

DataCollection collection

std::reverse_iterator<DataIterator> rBegin(iter) //iter is a DataIterator that's part-way through the collection
std::reverse_iterator<DataIterator> rEnd(collection.cbegin());

auto Found = std::find_if(
rBegin,
rEnd,
[](const Data& candidate){
return candidate.Value() == 0x00;
});

Когда я запускаю приведенный выше код, он никогда не находит объект Data, значение которого равно 0, хотя я знаю, что он существует. Когда я вставляю точку останова в предикат, я вижу странные значения, которые я никогда не ожидал бы увидеть как 0xCCCC — вероятно, неинициализированная память. Что происходит, так это то, что оператор разыменования reverse_iterator выглядит так (из xutility — Visual Studio 2010)

Data& operator*() const
{   // return designated value
DataIterator _Tmp = current;
return (*--_Tmp); //Here's the problem - the * operator on DataIterator returns a value instead of a reference
}

Последняя строка — где проблема — создаются временные данные и возвращается ссылка на эти данные. Ссылка недействительна сразу.

Если я изменю свой предикат в std :: find_if, чтобы принять (кандидат данных) вместо (постоянные данные)& кандидат) тогда предикат работает — но я уверен, что мне просто повезло с неопределенным поведением там. Ссылка недействительна, но я делаю копию данных до того, как память будет засорена.

Что я могу сделать?

  1. Исправьте мой DataIterator, чтобы оператор * возвращал данные& вместо данных? Я действительно не понимаю, как это возможно. Весь смысл в том, что мой DataIterator возвращает данные вместо данных& потому что у меня нет места для хранения всего несжатого набора данных в памяти, поэтому я создаю элементы, которые вы хотите просмотреть по требованию. Возможно, я мог бы удерживать текущее значение данных, но тогда эта ссылка станет недействительной в тот момент, когда вы увеличиваете или уменьшаете DataIterator. редактировать один из ответов предполагает shared_ptr
  2. Написать специализацию reverse_iterator и заставить его оператор разыменования возвращать значение вместо ссылки? Это кажется трудоемкой работой, но понятно, так как это мой DataIterator, который не играет здесь хорошо — не остальная часть STL.
  3. В том же духе, возможно, создайте find_if, который работает в обратном порядке — возможно, меньше работы, чем специализирующийся reverse_iterator.
  4. Что-то еще, о чем я не думал

Могу ли я что-то сделать с DataIterator, чтобы не дать кому-то еще полдня выяснить, что не так, когда они попробуют то же самое через 6 месяцев?

5

Решение

Не то чтобы я большой поклонник этой идеи, но если вы выделите кучу Data объект, а затем вернул ссылку на shared_ptr для него это позволило бы внешнему миру удерживать его дольше, если это необходимо, и для вас «забыть» об этом, когда вы сделаете шаг вперед.

С другой стороны, реализация вашего собственного нативного reverse_iterator может быть большая победа. Это то, что я сделал для своего собственного связанного списка, так как я не использовал объект-страж вроде gcc делает и не мог использовать std::reverse_iterator, Это действительно было не так сложно.

1

Другие решения

Это потому что reverse_iterator Интерфейс был разработан до существования decltype, Сегодня это будет написано как

auto operator*() const -> decltype(*current)
{   // return designated value
DataIterator _Tmp = current;
return (*--_Tmp);
}

а также в C ++ 14, даже конечный тип возврата не понадобится, так как он может быть выведен.

decltype(auto) operator*() const
{   // return designated value
DataIterator _Tmp = current;
return (*--_Tmp);
}
1

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

Я сделал специализацию reverse_iterator для DataIterator, который возвращает значение вместо ссылки. Это включало копирование / вставку реализации из xutility, указание одного из аргументов шаблона в качестве DataIterator и изменение

reference operator*() const

в

value operator*() const
0
По вопросам рекламы [email protected]