#c #memory-management #standards #typetraits #allocator
#c #управление памятью #стандарты #typetraits #распределитель
Вопрос:
Согласно http://en.cppreference.com/w/cpp/concept/Allocator , один класс, который может использоваться в качестве типа распределителя, должен соответствовать многим требованиям. Однако я не могу найти те же требования, что указаны в стандарте C . Стандарт требует, чтобы тип распределителя не должен быть типом, не относящимся к классу. (см. n3797 20.7.8.1)
Если у меня есть пустой класс Alloc
и полностью специализированный std::allocator_traits<Alloc>
, могу ли я использовать Alloc
следующим образом:
#include <vector>
struct Alloc {};
template<>
std::allocator_traits<Alloc>
{
// full of definitions as per the requirements for std::allocator_traits<T>
};
int main()
{
std::vector<int, Alloc> coll;
coll.push_back(8);
}
Комментарии:
1. Вы можете это сделать (в основном; вам все равно придется указывать
operator ==
и т.д.), Но я не вижу, что вы от этого выиграете, поскольку вам придется переопределять все в специализации, а не полагаться на значения по умолчанию.
Ответ №1:
Пока вы собираетесь использовать распределитель с контейнерами STL (или кодом, который придерживается тех же соглашений), вы можете определить свой распределитель, просто специализируясь std::allocator_traits
на вашем классе (для которого тогда достаточно просто определить; думая об этом, я думаю, вы могли бы даже перехватить существующий класс без распределителя для этой цели), поскольку вызовы вашего распределителя будут выполняться не напрямую, а через std::allocator_traits
специализацию.
Однако, похоже, что это противоречит назначению этого шаблона класса, поскольку, определяя эту спецификацию, вы теряете все значения по умолчанию, установленные шаблоном, поэтому вам приходится переопределять все, что не проще, чем определять все в вашем Alloc
классе напрямую. В частности, 17.6.4.2.1, похоже, делает неопределенным поведение, чтобы просто специализировать отдельные функции-члены, такие как std::allocator_traits<Alloc>::allocate
, которые, конечно, должны быть определены.
Предполагаемое использование явно заключается в том, чтобы вообще не специфицировать std::allocator_traits
, а просто определить те элементы Alloc
, для которых не предусмотрены значения по умолчанию (по существу value_type
, allocate
и deallocate
; в конце 17.6.3.5 есть пример struct SimpleAllocator
, который предоставляет минимальный рабочий интерфейс), или для которых вы хотите переопределить значения по умолчанию ( std::allocator_traits
настроен на использование любых определенных элементов с предпочтением по умолчанию).
В качестве личного вопроса я бы посоветовал обычно всегда переопределять typedefs propagate_on_container_move_assignment
и propagate_on_container_swap
(но не их copy_assignment
относительный) из их false_type
состояния по умолчанию в true_type
. Поскольку шаблоны контейнерных классов не могут разрешить ассоциировать неэквивалентный экземпляр распределителя с существующей памятью (что в конечном итоге приведет к ее неправильному освобождению), они должны быть готовы отказаться от семантики перемещения и перераспределить все в случае, если (соответствующий из) два упомянутых типа являются, false_type
и экземпляры распределителя оказываются неравными. Возможно, компилятор достаточно умен, чтобы определить, что для вашего типа распределителя (без состояния) экземпляры могут никогда не сравнивать неравные экземпляры, и рассматривать вышеупомянутое усложнение «быть готовым» как мертвый код. Однако эту обработку мертвого кода легче обнаружить, и это с большей вероятностью произойдет, если этот тип является true_type
, и тогда он также будет применяться для распределителей с полным состоянием (разрешающих распространение). Указано по-другому, я думаю, что эти два значения по умолчанию просто выбраны неправильно.