Требуется ли явно создавать каждый член массива char? - PullRequest
3 голосов
/ 16 октября 2019

Рассмотрим следующий код:

#include <memory>

template<typename Allocator>
char* allocate_array(Allocator alloc, size_t n)
{
    static_assert(std::is_same<typename Allocator::value_type, char>::value);
    char* data = alloc.allocate(n);

    // (1)
    for (size_t i=0; i<n; ++i) {
        new (data+i) char();
    }

    return data;
}

char* copy_array(size_t n, const char* original)
{
    char* copy = allocate_array(std::allocator<char>(), n);
    // (2)
    for (size_t i=0; i<n; ++i) {
        copy[i] = original[i];
    }
    return copy;
}

Является ли инициализация новой позиции, отмеченная (1), необходимой для предотвращения неопределенного поведения программы, даже если гарантируется, что каждый символ будет записан в (2) прежде чем его можно будет прочитать? Или это можно безопасно удалить?

Обратите внимание, что это не оптимизируется, даже на -O3 Я вижу, как gcc и clang генерируют последующие вызовы к memset() и memcpy().

1 Ответ

2 голосов
/ 16 октября 2019

Является ли инициализация нового размещения, отмеченная (1), необходимой для предотвращения неопределенного поведения программы.

Технически, насколько я могу судить.

[basic.life]

Время жизни объекта типа T начинается, когда:

  • хранилище с правильным выравниванием и размером для типа T, и
  • его инициализация (если есть) завершена (включая пустую инициализацию) ([dcl.init]),

... до времени жизниобъект запущен, но после того, как хранилище, которое будет занимать объект, было выделено или, после того, как закончился срок службы объекта, и до того, как хранилище, которое занял объект, используется повторно или освобождено, может использоваться любое значение glvalue, которое ссылается на исходный объект, нотолько ограниченным образом. ... Программа имеет неопределенное поведение , если:

  • glvalue используется для доступа к объекту или
  • ...

Обратите внимание, что это не оптимизируется вне

Вместо значения, инициализирующего символы, вы можете инициализировать по умолчанию. Тогда их значение останется неопределенным до назначения, и, следовательно, не будет необходимости в memset. Также для этого есть стандартная функция, поэтому нет необходимости писать цикл:

std::uninitialized_default_construct(data, data + n);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...