Dominic Price
Dominic Price

Reputation: 1146

Make iterator that can be used to initialize vector

I'm having some trouble creating an iterator type for my class which can be used to initialize a vector. Probably best explained with some code, here's an example of what my implementation looks like:

#include <tuple>
#include <cstdint>

struct Foo
{
public:
    Foo(uint8_t i) : i(i) {}
    struct iterator
    {
    public:
        using value_type = std::pair<int, bool>;
        using reference = value_type;
        using pointer = value_type*;
        using iterator_category = std::input_iterator_tag;

        bool operator == (const iterator& other) { return cur == other.cur; }
        bool operator != (const iterator& other) { return !(*this == other); }

        iterator& operator ++ () { if (cur > -1) --cur; return *this; }
        iterator operator ++ (int) { iterator tmp = *this; ++(*this); return tmp; }

        reference operator * () { return std::make_pair<int, bool>(8 - cur, foo->i & (1 << cur)); }
        pointer operator -> () { static value_type v; v = *(*this); return &v; }
    private:
        friend Foo;
        iterator(const Foo* foo, int start) : foo(foo), cur(start) {}
        const Foo* foo;
        int cur;
    };

    iterator begin() const { return iterator(this, 7); }
    iterator end() const { return iterator(this, -1); }

    uint8_t i;
};

The logic of what the class is doing doesn't matter; it's the fact that although I can use this iterator in a for loop, I get an error when attempting to construct a vector from it:

#include <iostream>
#include <vector>

// struct Foo...

int main()
{
    Foo foo(73);

    // Works, output as expected
    for (auto elem : foo)
        std::cout << "Position " << elem.first << " is a " << elem.second << '\n';

    // Works, output as expected
    for (auto it = foo.begin(), end = foo.end(); it != end; ++it)
        std::cout << "Position " << it->first << " is a " << it->second << '\n';

    // Error: cannot convert argument 1 from 'PowersOf2::iterator' to 'const unsigned __int64'
    std::vector<std::pair<int, bool>> v(foo.begin(), foo.end());
}

cppreference tells me that std::vector's constructor takes two iterators if InputIt satisfies InputIterator. It also tells me that the requirements for an InputIterator are

  1. Satisfies Iterator
  2. Satisfies EqualityComparable
  3. i != j, *i, i->m, ++i, (void)i++ and *i++ are valid

so I'm not sure what's gone wrong. Any help appreciated!

Upvotes: 4

Views: 341

Answers (1)

Fran&#231;ois Andrieux
Fran&#231;ois Andrieux

Reputation: 29023

According to cccpreference.com the std::vector constructor that takes iterators "[...] only participates in overload resolution if InputIt satisfies InputIterator, to avoid ambiguity with the overload (2).".

To satisfy InputIterator a type must satisfy Iterator. In turn, Iterator requires the type to provide several type alias, including difference_type which you've omitted.

Try adding the public type alias using difference_type = std::ptrdiff_t; to your iterator type.

Upvotes: 3

Related Questions