Пользовательский распределитель для std::vector <char> игнорируется

Я пытался использовать пользовательский распределитель для std::vector<char>, но я заметил, что std::vector не требует/не использует какую-либо из функций-членов из моего распределителя. Как это возможно?

#include <vector>

struct A : private std::allocator<char> {
   typedef std::allocator<char> alloc;
   using alloc::value_type;
   using alloc::pointer;
   using alloc::const_pointer;
   using alloc::difference_type;
   using alloc::size_type;
   using alloc::rebind;
   // member functions have been removed, since the program compiles without them
};

int main() {
    std::vector<char, A> v;
    v.resize(4000);
    for (auto& c : v)
      if (c)
         return 1; // never happens in my environment
   return 0; // all elements initialized to 0. How is this possible?
}

Я пытался выполнить вышеуказанную программу с помощью онлайн-компилятора С++ 11 (LiveWorkSpace), предоставляя g++ 4.7.2, 4.8 и 4.6.3.

В основном allocate(), deallocate(), construct() и destroy() не определены в моем распределителе, но программа компилируется, и все элементы будут инициализированы до 0.

Ответ 1

Стандартная библиотека GCC всегда будет переустанавливать поставляемый распределитель, поэтому внутри он делает что-то вроде этого (в С++ 03):

typedef Alloc::template rebind<value_type>::other _Allocator_type;

(В С++ 11 он использует allocator_traits, но в этом случае результат тот же.)

Затем вектор хранит объект этого типа внутри и использует его для выделения всех (de).

Поскольку вы не определили шаблон элемента rebind в своем распределителе, вы только что обновили его из базового класса, результатом восстановления является std::allocator<value_type>, а не ваш собственный тип. std::allocator, конечно, предоставляет все эти функции, поэтому те, которые используются, независимо от того, определяете ли вы их по своему типу.

Вы можете исправить это, добавив это в свой распределитель вместо using alloc::rebind;, чтобы vector сохранял и использовал A внутри:

struct A : private std::allocator<char> {
    template<typename U>
      struct rebind {
        typedef A other;
      };

N.B. это будет работать только для vector, потому что vector не нужно строго переустанавливать распределитель (пользователям требуется создать экземпляр шаблона с помощью allocator<value_type>, но GCC vector все равно перепроверяет, так что если пользователь создает экземпляр vector<int, std::allocator<char>> он по-прежнему работает.) Для контейнеров на основе node, таких как std::set, ваш распределитель должен быть шаблоном, который можно отскочить, потому что контейнеру необходимо выделять его внутренние типы node, а не value_type, поэтому ему нужно Alloc::rebind<internal_node_type>::other.

Ответ 2

vector будет переназначать распределитель. Когда вы введете его в область видимости от std::allocator, A::rebind<T>::other будет просто std::allocator<T>. Так что все работает нормально.