2013-03-05 2 views
10

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

#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? 
} 

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

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

ответ

14

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

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

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

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

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

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

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

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

+1

На самом деле, почему желательно, чтобы он по-прежнему работал, если пользователь предоставляет 'std :: allocator ' в качестве распределителя для вектора '? Разве не имеет смысла получать ошибку компилятора? –

+1

Я не уверен, что это желательно лично, но это всегда было так. Для других контейнеров имеет смысл: если пользователь говорит 'std :: map , std :: allocator >>' это удобство принимать его, хотя технически распределитель должен быть 'std :: allocator >' –

+5

@AndyProwl - распределители действительно должны быть параметрами шаблона шаблона, но параметры шаблона шаблона не существовали в то время, когда STL (ПРИМЕЧАНИЕ: ** STL **, а не ** Стандартная библиотека **) была разработана, поэтому была создана «rebind». –

7

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