Dewfy
Dewfy

Reputation: 23644

MSVC++ 17 std::copy expects "operator -" for custom iterator

I migrate my project from VC++ 2015 to VC++ 2017. I have my own implementation of iterator. It has forward-only operations set:

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

Somewhere in code I use it for std::copy

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

This line perfectly worked in VC++2015 but fails in 2017 just because:

error C2784: 'unknown-type std::operator -(const std::move_iterator<_RanIt> &,const std::move_iterator<_RanIt2> &)': could not deduce template argument for 'const std::move_iterator<_RanIt> &' from 'const NodeTableIterator...'

Simple inspection inside std::copy shows this source of error:

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

Where _Idl_distance really expects that my iterator should support operator -. Have you any idea how to overcome this strange requirement?

Upvotes: 3

Views: 97

Answers (2)

L. F.
L. F.

Reputation: 20639

You need to provide the five types, either within the class itself:

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

or within a custom specialization of std::iterator_traits. Otherwise, the tag dispatch on the iterator category in the standard algorithms won't work.


Reference: [iterator.traits]/1 (the types in std::iterator_traits are required to be present)

To implement algorithms only in terms of iterators, it is often necessary to determine the value and difference types that correspond to a particular iterator type. Accordingly, it is required that if Iterator is the type of an iterator, the types

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

be defined as the iterator's difference type, value type and iterator category, respectively. In addition, the types

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

shall be defined as the iterator's reference and pointer types, that is, for an iterator object a, the same type as the type of *a and a->, respectively. In the case of an output iterator, the types

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

may be defined as void.

[iterator.traits]/2 (the types in std::iterator_traits can be generated from the member types)

If Iterator has valid ([temp.deduct]) member types difference_­type, value_­type, pointer, reference, and iterator_­category, iterator_­traits<Iterator> shall have the following as publicly accessible members:

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;

Otherwise, iterator_­traits<Iterator> shall have no members by any of the above names.

Upvotes: 6

eerorika
eerorika

Reputation: 238431

You'll need to define NodeTableIterator::iterator_category. More specifically, it must not be std::random_access_iterator_tag or else operator- will be required by the algorithm.

You also need to define

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

Or else std::iterator_traits won't have any of those definitions. Or you can spesialise std::iterator_traits explicitly. Note that if you don't do either of these things, then NodeTableIterator is not an Iterator at all.

Upvotes: 3

Related Questions