Подтвердить что ты не робот

Почему не выделяется этот распределитель S ++ STL?

Я пытаюсь написать специализированный распределитель STL, который получен из std::allocator, но каким-то образом все вызовы allocate() переходят к базовому классу. Я сузил его до этого кода:

template <typename T> class a : public std::allocator<T> {
public:
    T* allocate(size_t n, const void* hint = 0) const {
        cout << "yo!";
        return 0;
    }
};

int main()
{
    vector<int, a<int>> v(1000, 42);
    return 0;
}

Я ожидаю "Yo!" чтобы печатать, а затем какая-то ужасная ошибка, потому что я фактически ничего не выделяю. Вместо этого программа работает нормально и ничего не печатает. Что я делаю неправильно?

Я получаю те же результаты в gcc и VS2008.

4b9b3361

Ответ 1

Вам нужно будет предоставить шаблон члена обратной связи и другие элементы, перечисленные в требованиях распределителя в стандарте С++. Например, вам нужен конструктор копии шаблона, который принимает не только allocator<T>, но также allocator<U>. Например, может выполняться один код, который, вероятно, будет выглядеть, например, std:: list,

template<typename Allocator>
void alloc1chunk(Allocator const& alloc) {
    typename Allocator::template rebind<
        wrapper<typename Allocator::value_type>
      >::other ot(alloc);
    // ...
}

Код не будет работать, если не существует правильного шаблона перезаписи или нет соответствующего конструктора копирования. Вам ничего не пригодится, если угадать, каковы требования. Рано или поздно вам придется делать код, который опирается на одну часть этих требований распределителя, а код не будет работать, потому что ваш распределитель нарушает их. Я рекомендую вам взглянуть на них в каком-нибудь рабочем проекте вашей копии Стандарта в 20.1.5.

Ответ 2

В этом случае проблема заключается в том, что я не переопределил элемент повторной привязки распределителя. Эта версия работает (в VS2008):

template <typename T> class a : public std::allocator<T> {
public:
    T* allocate(size_t n, const void* hint = 0) const {
        cout << "yo!";
        return 0;
    }

    template <typename U> struct rebind
    {
        typedef a<U> other;
    };
};

int main() {
    vector<int, a<int>> v(1000, 42);
    return 0;
}

Я нашел это, отлаживая заголовки STL.

Будет ли это работать или нет, будет полностью зависеть от реализации STL, хотя, я думаю, что в конечном итоге, Klaim прав, что это не должно быть сделано таким образом.

Ответ 3

У меня есть два шаблона для создания настраиваемых распределителей; первый работает автоматически, если он используется в пользовательском типе:

template<>
class std::allocator<MY_TYPE>
{
public:
    typedef size_t      size_type;
    typedef ptrdiff_t   difference_type;
    typedef MY_TYPE*    pointer;
    typedef const MY_TYPE*  const_pointer;
    typedef MY_TYPE&    reference;
    typedef const MY_TYPE&  const_reference;
    typedef MY_TYPE     value_type;

    template <class U>
    struct rebind
    {
        typedef std::allocator<U> other;
    };

    pointer allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
    {
        return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
    }
    void construct(pointer p, const_reference val)
    {
        ::new(p) T(val);
    }
    void destroy(pointer p)
    {
        p->~T();
    }
    void deallocate(pointer p, size_type n)
    {
        FREE_FUNC(p);
    }
    size_type max_size() const throw()
    {
        // return ~size_type(0); -- Error, fixed according to Constantin comment
        return std::numeric_limits<size_t>::max()/sizeof(MY_TYPE);
    }
};

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

    namespace MY_NAMESPACE
    {

    template <class T> class allocator;

    // specialize for void:
    template <>
    class allocator<void>
    {
    public:
        typedef void*       pointer;
        typedef const void* const_pointer;
        // reference to void members are impossible.
        typedef void        value_type;

        template <class U>
        struct rebind
        {
            typedef allocator<U> other;
        };
    };

    template <class T>
    class allocator
    {
    public:
        typedef size_t      size_type;
        typedef ptrdiff_t   difference_type;
        typedef T*      pointer;
        typedef const T*    const_pointer;
        typedef T&      reference;
        typedef const T&    const_reference;
        typedef T       value_type;

        template <class U>
        struct rebind
        {
            typedef allocator<U> other;
        };

        allocator() throw()
        {
        }
        template <class U>
        allocator(const allocator<U>& u) throw()
        {
        }
        ~allocator() throw()
        {
        }

        pointer address(reference r) const
        {
            return &r;
        }
        const_pointer address(const_reference r) const
        {
            return &r;
        }
        size_type max_size() const throw()
        {
            // return ~size_type(0); -- Error, fixed according to Constantin comment
            return std::numeric_limits<size_t>::max()/sizeof(T);
        }
        pointer allocate(size_type n, allocator<void>::const_pointer hint = 0)
        {
            return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
        }
        void deallocate(pointer p, size_type n)
        {
            FREE_FUNC(p);
        }

        void construct(pointer p, const_reference val)
        {
            ::new(p) T(val);
        }
        void destroy(pointer p)
        {
            p->~T();
        }
    };

template <class T1, class T2>
inline
bool operator==(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
    return true;
}

template <class T1, class T2>
inline
bool operator!=(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
    return false;
}

}

Первый шаблон выше для вашего собственного определенного типа не требует дополнительной обработки, а автоматически используется стандартными классами контейнеров. Второй шаблон требует дальнейшей работы при использовании стандартного типа. Например, для std::string нужно использовать следующую конструкцию при объявлении переменных этого типа (это проще всего с typedef):

std::basic_string<char>, std::char_traits<char>, MY_NAMESPACE::allocator<char> >

Ответ 4

Следующий код печатает "yo", как ожидалось, - то, что вы видели, было нашим старым другом "поведение undefined".

#include <iostream>
#include <vector>
using namespace std;

template <typename T> class a : public std::allocator<T> {
public:
    T* allocate(size_t n, const void* hint = 0) const {
        cout << "yo!";
        return new T[10000];
    }
};

int main()
{
    vector<int, a<int> > v(1000, 42);
    return 0;
}

Изменить: я только что проверил стандарт С++ относительно распределителя по умолчанию. Нет никакого запрета на наследство от него. На самом деле, насколько мне известно, такого запрета в какой-либо части Стандарта нет.