Инициализированные значения объектов в конструкторах С++ 11 и std::vector

В С++ существует несколько веских причин использовать массив C над std::vector. Одной из немногих убедительных причин, по крайней мере, с С++ 03, был тот факт, что невозможно использовать вектор для выделения неинициализированного массива объектов. Конструктор "fill" для std::vector:

vector(size_type count, const T& value = T())

Значит, что...

int* array = new array[1000000];

вероятно, будет намного более эффективным, чем:

std::vector<int> v(1000000);

... так как вектор-конструктор должен будет нуль инициализировать массив целых чисел. Таким образом, при работе с вектором POD нет реального эквивалента malloc; лучшее, что вы можете получить, эквивалентно calloc.

С++ 11, похоже, изменил это, с понятием "инициализация ценности". В С++ 11 std::vector имеет новый конструктор, который принимает единственное значение size_type без аргумента по умолчанию. Это "значение-инициализирует" все элементы вектора. Стандарт С++ 11 различает "инициализацию значения" и "нулевую инициализацию".

Я понимаю, что "инициализация значений" эквивалентна вызову конструктора по умолчанию на T. Если T - тип POD, такой как int, то конструктор по умолчанию просто создает неинициализированное целое число. Таким образом, в С++ 11 explicit vector::vector(size_type count) действительно эквивалентен malloc, если T является POD.

Однако мое понимание этого основано на проекте стандарта С++ 11, а не на окончательном стандарте.

Вопрос. Является ли мое понимание правильным здесь? Предоставляет ли explicit vector::vector(size_type count) неинициализированный массив (похожий на malloc), если T является POD?

Ответ 1

Вопрос. Является ли мое понимание правильным здесь? Предоставляет ли explicit vector::vector(size_type count) неинициализированный массив (аналогично malloc), если T является POD?

Нет. Здесь есть разница между С++ 03 и С++ 11, но это не так. Разница в том, что в С++ 03, vector<T>(N) будет по умолчанию строить a T, а затем сделать N его копии для заполнения вектора.

В то время как в С++ 11, vector<T>(N) будет заполнять вектор по умолчанию, построив T N раз. Для типов POD эффект идентичен. Действительно, я ожидал бы, что для почти всех типов эффект будет идентичным. Однако для чего-то вроде unique_ptr (тип только для перемещения) разница имеет решающее значение. Семантика С++ 03 никогда не будет работать, так как вы не можете сделать копию типа только для перемещения.

Итак:

vector<unique_ptr<int>> v(10);

создает вектор из 10 null unique_ptrs (которые не являются копиями друг друга).

В редком случае, когда это имеет значение, и вам нужно поведение С++ 03, которое можно легко выполнить с помощью:

vector<T> v(10, T());

Ответ 2

Примечание: инициализация значения происходит в распределителе, поэтому, если вы хотите, чтобы вектор выполнял инициализацию по умолчанию вместо инициализации значения для построенных по умолчанию элементов, вы можете сделать что-то вроде:

template<typename T>
struct DefaultInitAllocator {
    template<typename U>
    void construct(U* p)
    { ::new (static_cast<void*>(p)) U; }

    template<typename U, typename... Args>
    void construct(U* p, Args&&... args)
    { ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...); }

    // ... rest of the allocator interface
};

// ...
typedef std::vector<int, DefaultInitAllocator<int>> DefaultInitVectorInt;