Должен ли allocator construct() инициализировать по умолчанию вместо инициализации значения?
В качестве продолжения этого вопроса для реализации construct
требуется распределитель по умолчанию (std::allocator<T>
) (согласно [default.allocator])
template <class U, class... Args>
void construct(U* p, Args&&... args);
Эффекты: ::new((void *)p) U(std::forward<Args>(args)...)
То есть всегда инициализация значений. Результатом этого является то, что std::vector<POD> v(num)
, для любого типа pod, будет инициализировать значение num
элементов, что дороже, чем инициализация по умолчанию элементов num
.
Почему & dagger;std::allocator
не предоставил дополнительную перегрузку по умолчанию? То есть, что-то вроде (заимствовано из Кейси):
template <class U>
void construct(U* p) noexcept(std::is_nothrow_default_constructible<U>::value)
{
::new(static_cast<void*>(p)) U;
}
Была ли причина предпочесть инициализацию значения в случаях вызова? Мне кажется удивительным, что это нарушает обычные правила С++, где мы платим только за то, что хотим использовать.
& dagger; Я предполагаю, что такое изменение невозможно в будущем, учитывая, что в настоящее время std::vector<int> v(100)
предоставит вам 100 0
s, но мне интересно, почему это так... учитывая, что так же легко потребовалось бы std::vector<int> v2(100, 0)
таким же образом, что существуют различия между new int[100]
и new int[100]{}
.
Ответы
Ответ 1
В С++ 03 Участник-распределитель construct
принял два аргумента: указатель и значение, которое было использовано для выполнения инициализации копирования:
20.1.6 Таблица 34
a.construct(p,t)
Эффект:
::new((void*)p) T(t)
construct
взятие двух параметров может быть прослежена до 1994 года (стр. 18). Как вы можете видеть, в концепции orignal Степанова это не было частью интерфейса распределителя (он не должен был настраиваться) и присутствовал как оболочка поверх размещения new.
Единственный способ узнать наверняка - спросить самого Степанова, но я полагаю, что эта причина была следующей: если вы хотите что-то построить, вы хотите инициализировать ее с определенным значением. И если вы хотите, чтобы ваши целые числа не инициализировались, вы можете просто опустить вызов construct
, поскольку он не нужен для типов POD. Позже construct
и другие связанные функции были объединены в распределители, и контейнеры были параметризованы на них, что привело к некоторой потере контроля при инициализации для конечного пользователя.
Таким образом, кажется, что отсутствие инициализации по умолчанию имеет исторические причины: никто, хотя о его важности, когда С++ был стандартизован, а более поздние версии Стандарта не вносили изменения в разрыв.