Search code examples
c++gccstlvisual-studio-2013const-iterator

STL const_iterator cast -- compiler differences


I'm porting a big chunk of code from gcc to Visual Studio 2013. The following code sample works fine (!) on gcc 4.4, but compiling begin() and end() fails on VS2013 with:

error C2440: '' : cannot convert from 'unsigned char *' to 'std::_Vector_const_iterator>>'

class foo {
    unsigned char* value;
    int length;

    std::vector<unsigned char>::const_iterator begin();
    std::vector<unsigned char>::const_iterator end();
};

std::vector<unsigned char>::const_iterator foo::begin() {
    return std::vector<unsigned char>::const_iterator(value);
}

std::vector<unsigned char>::const_iterator foo::end() {
    return std::vector<unsigned char>::const_iterator(value + length);
}

Given that I don't want to rewrite the whole thing, is there a portable way to create these const_iterators?


Solution

  • There is no portable way to do what you're attempting because there is no requirement that a (const_)iterator be constructible from a pointer to the underlying value type. libstdc++ happens to provide such a constructor but the VS standard library implementation doesn't. Instead, its (const_)iterator constructor takes a pointer to the underlying value type and a pointer to the container itself, which it uses to perform additional validation during debug builds.

    The easiest solution is to replace std::vector<unsigned char>::const_iterator with unsigned char const *. A raw pointer falls in the RandomAccessIterator category, which is the same as vector::(const_)iterators.

    unsigned char const *foo::begin() {
        return value;
    }
    
    unsigned char const *foo::end() {
        return value + length;
    }
    

    If you needs the iterator to be a class type, then you'll need to create a custom iterator. While this can be done from scratch, it's a lot easier to use Boost.IteratorFacade, which will provide a bunch of the necessary boilerplate that goes into constructing a custom iterator.

    #include <boost/iterator/iterator_facade.hpp>
    
    struct const_foo_iterator : boost::iterator_facade<const_foo_iterator,
                                                        unsigned char const,
                                                        boost::random_access_traversal_tag>
    {
      const_foo_iterator() = default;
      const_foo_iterator(unsigned char const *iter) : iter(iter) {}
    private:
        friend class boost::iterator_core_access;
    
        void increment() { ++iter; }
        void decrement() { --iter; }
        void advance(std::ptrdiff_t n) { iter += n; }
    
        std::ptrdiff_t distance_to(const_foo_iterator const& other) const
        { return iter - other.iter; }
    
        bool equal(const_foo_iterator const& other) const
        { return this->iter == other.iter; }
    
        unsigned char const& dereference() const { return *iter; }
        unsigned char const* iter = nullptr;
    };
    
    const_foo_iterator foo::begin() {
        return value;
    }
    
    const_foo_iterator foo::end() {
        return value + length;
    }
    
    static_assert(std::is_same<std::iterator_traits<const_foo_iterator>::value_type,
                               unsigned char>::value, "value_type");
    static_assert(std::is_same<std::iterator_traits<const_foo_iterator>::pointer,
                               unsigned char const *>::value, "pointer");
    static_assert(std::is_same<std::iterator_traits<const_foo_iterator>::iterator_category,
                               std::random_access_iterator_tag>::value, "iterator_category");
    

    Live demo