c++gcciteratorallocator

What is the problem when using a custom allocator in c++, when assigning the container to an iterator?


I have the problem that GCC is complaining about a type mismatch when compiling the following code:

#include <iostream>
#include <cstdint>
#include <vector>
#include <limits>

template <class T>
struct Mallocator {
    typedef T value_type;

    Mallocator() = default;

    template <class U>
    Mallocator(const Mallocator<U>&) {}

    T* allocate(std::size_t n) {
        if (n > std::numeric_limits<std::size_t>::max() / sizeof(T))
            throw "std::bad_array_new_length";

        if (auto p = static_cast<T*>(malloc(n * sizeof(T)))) {
            report(p, n);
            return p;
        }

        throw std::bad_alloc();
    }

    void deallocate(T* p, std::size_t n) {
        report(p, n, 0);
        free(p);
    }

   private:
    void report(T* p, std::size_t n, bool alloc = true) const {
        std::cout << (alloc ? "Alloc: " : "Dealloc: ") << sizeof(T) * n
                  << " bytes at " << std::hex << std::showbase
                  << reinterpret_cast<void*>(p) << std::dec << '\n';
    }
};

struct Foo
{
    Foo(int f): m_values{f} {}
    std::vector<int, Mallocator<int> > m_values;
    std::vector<int>::iterator begin() { return m_values.begin(); }
};

int main() {
    Foo f(1);
    return 0;
}

MSVC and Clang (with libc++ enabled) compile fine for the same code. Isn't there a problem, when the iterator hops to the next values? How can it know, where in memory is the next value to iterate, if the allocator allocates different sizes than the standard allocator?


Solution

  • std::vector<int> is one type, std::vector<int, Mallocator<int> > is a different type. It is unspecified whether std::vector<int, Mallocator<int> >::iterator is convertible to std::vector<int>::iterator.

    A simple way they could be incompatible is they are defined within vector, and so are themselves unrelated types.

    A simple way they could be compatible is that they are defined outside vector and aliased within it, and only depend on the element type parameter.

    The very simple fix is for Foo to use the correct type in it's declaration.

    struct Foo
    {
        Foo(int f): m_values{f} {}
        std::vector<int, Mallocator<int> > m_values;
        std::vector<int, Mallocator<int> >::iterator begin() { return m_values.begin(); }
    };