2016-03-09 2 views
12

В продолжение к this question, Распределитель по умолчанию (std::allocator<T>) требуется для реализации construct следующим образом (в соответствии с [default.allocator]):Должен ли allocator construct() инициализировать по умолчанию вместо инициализации значения?

template <class U, class... Args> 
void construct(U* p, Args&&... args); 

Эффекты: ::new((void *)p) U(std::forward<Args>(args)...)

То есть всегда инициализация значений. Результатом этого является то, что std::vector<POD> v(num), для любого типа подкачки, будет стоить-инициализировать элементы num, что является более дорогостоящим, чем инициализация по умолчанию num элементов.

Почему не & dagger;std::allocator обеспечивают дополнительную перезагрузку по умолчанию? То есть, что-то вроде (заимствованное из Casey):

template <class U> 
void construct(U* p) noexcept(std::is_nothrow_default_constructible<U>::value) 
{ 
    ::new(static_cast<void*>(p)) U; 
} 

Была ли причина предпочесть инициализации значения в случаях вызова? Мне кажется удивительным, что это нарушает обычные правила C++, где мы платим только за то, что хотим использовать.


& dagger; Я предполагаю, что такое изменение невозможно в будущем, учитывая, что в настоящее время std::vector<int> v(100) предоставит вам 100 0, но мне интересно, почему это так ... учитывая, что так же легко потребовалось бы std::vector<int> v2(100, 0) так же, как существуют различия между new int[100] и new int[100]{}.

+0

См. [P0040] (http: //www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0040r3.html). – FrankHB

+0

@FrankHB Изменит ли этот документ какой бы «вектор» здесь? Это просто добавляет алгоритмы в стандартную библиотеку? – Barry

+0

Право. Так что это не ответ, просто комментарий. Он обеспечивает удобный интерфейс для облегчения реализации вашего обходного пути. – FrankHB

ответ

2

В C++ 03 распределителей construct член взял два аргумента: указатель и значение, которое используется для выполнения копирования-инициализации:

20.1.6 Таблица 3

a.construct(p,t)

Эффект:
::new((void*)p) T(t)

construct с двумя параметрами может быть traced back to 1994 (стр. 18). Как вы можете видеть, в концепциях Orignal Степанова это не было частью интерфейса распределителя (он не должен был настраиваться) и присутствовал как оболочка поверх размещения new.

Единственный способ узнать наверняка - спросить самого Степанова, но я полагаю, что причина заключалась в следующем: если вы хотите что-то построить, вы хотите инициализировать его с определенным значением. И если вы хотите, чтобы ваши целые числа не инициализировались, вы можете просто опустить вызов construct, поскольку он не нужен для типов POD. Позже construct и другие связанные функции были объединены в распределители, а контейнеры были параметризованы на них, что привело к некоторой потере контроля при инициализации для конечного пользователя.

Так что, по-видимому, отсутствие инициализации по умолчанию имеет исторические причины: никто, хотя о его важности, когда C++ был стандартизован, а более поздние версии Стандарта не вносили изменения в разрыв.

Смежные вопросы