MSVC ++ 17 std :: copy ожидает "operator -" для пользовательского итератора - PullRequest
3 голосов
/ 16 октября 2019

Я перенесу свой проект из VC ++ 2015 в VC ++ 2017. У меня есть собственная реализация итератора. У него есть только операции пересылки:

template <class Container>
struct NodeTableIterator
{
    typedef NodeTableIterator<Container> this_t;
    this_t& operator ++();
    this_t operator ++(int);
};

Где-то в коде, который я использую для std::copy

std::copy(tbl.begin(), tbl.end(),
        std::ostream_iterator<int>(std::cout, ", "));    

Эта строка отлично работала в VC ++ 2015, но завершается сбоем только в 2017 годупотому что:

ошибка C2784: 'неизвестный тип std :: operator - (const std :: move_iterator <_RanIt> &, const std :: move_iterator <_RanIt2> &)': не удалось вывести шаблонаргумент для 'const std :: move_iterator <_RanIt> &' from 'const NodeTableIterator ...'

Простая проверка внутри std::copy показывает этот источник ошибки:

    const auto _UDest = _Unchecked_n(_Dest, _Idl_distance<_InIt>(_UFirst, _ULast));

Где _Idl_distance действительно ожидает, что мой итератор должен поддерживать operator -. Ты хоть представляешь, как преодолеть это странное требование?

Ответы [ 2 ]

6 голосов
/ 16 октября 2019

Вам необходимо указать пять типов, либо внутри самого класса:

template <class Container>
struct NodeTableIterator {
    using iterator_category = /* ... */;
    using value_type = /* ... */;
    using difference_type = /* ... */;
    using pointer = /* ... */;
    using reference = /* ... */;
    // ...
};

, либо в рамках пользовательской специализации std::iterator_traits. В противном случае отправка тега для категории итераторов в стандартных алгоритмах не будет работать.


Ссылка: [iterator.traits] / 1 (типы в std::iterator_traits:должен присутствовать)

Чтобы реализовать алгоритмы только в терминах итераторов, часто необходимо определить типы значений и разностей, которые соответствуют конкретному типу итераторов. Соответственно, требуется, чтобы, если Iterator был типом итератора, типы

iterator_traits<Iterator>::difference_type
iterator_traits<Iterator>::value_type
iterator_traits<Iterator>::iterator_category

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

iterator_traits<Iterator>::reference
iterator_traits<Iterator>::pointer

должны быть определены как ссылочный тип и указатель итератора, то есть для объекта итератора a тот же тип, что и тип *a и a->соответственно. В случае выходного итератора типы

iterator_traits<Iterator>::difference_type
iterator_traits<Iterator>::value_type
iterator_traits<Iterator>::reference
iterator_traits<Iterator>::pointer

могут быть определены как void.

[iterator.traits] / 2 (типы в std::iterator_traits могут быть сгенерированы из типов элементов)

Если Iterator имеет действительные ([temp.deduct]) типы элементов difference_­type, value_­type, pointer,reference и iterator_­category, iterator_­traits<Iterator> должны иметь в качестве общедоступных членов следующее:

using difference_type   = typename Iterator::difference_type;
using value_type        = typename Iterator::value_type;
using pointer           = typename Iterator::pointer;
using reference         = typename Iterator::reference;
using iterator_category = typename Iterator::iterator_category;

В противном случае iterator_­traits<Iterator> не должно иметь членов с любым из указанных выше имен.

3 голосов
/ 16 октября 2019

Вам нужно будет определить NodeTableIterator::iterator_category. Более конкретно, оно не должно быть std::random_access_iterator_tag, иначе алгоритму потребуется operator-.

Вам также нужно определить

NodeTableIterator::difference_type
NodeTableIterator::value_type
NodeTableIterator::pointer
NodeTableIterator::reference

Иначе std::iterator_traits не будетесть любое из этих определений. Или вы можете явно указать std::iterator_traits. Обратите внимание, что если вы не сделаете ни одну из этих вещей, то NodeTableIterator вовсе не будет Iterator.

...