Моя задача — создать массив типов шаблонов без вызова конструктора T (шаблон). После этого я хочу переместить одно значение в этот массив из другого с помощью std :: move. Как я могу сделать это в C ++? Вот мой код:
void *temp = malloc((end-begin) * sizeof(T));
for (unsigned int i = begin; i < end; ++i){
temp[i] = std::move(array[i]);
}
Но это не работа. Компилятор говорит следующее: Индекс указателя на неполный тип ‘void’.
Массив void
не имеет смысла. void
является неполным типом (и не имеет размера), поэтому temp[i]
генерирует ошибку, которую вы видите.
Чтобы достичь того, что вы хотите сделать, почему бы не использовать std::vector
, и вставлять в него предметы по мере их появления?
std::vector<T> temp;
for (unsigned int i = begin; i < end; ++i){
temp.push_back(std::move(array[i]));
}
Если вы действительно хотите сделать это вручную, вы ищете размещение нового. Сначала вы выделяете «сырой буфер» unsigned char
s:
unsigned char* buf = new unsigned char(end-begin);
или же:
auto buf = std::make_unique<unsigned char[]>(end-begin);
а потом ты пишешь такие вещи, как new (buf) T
, new (buf+sizeof(T)) T
, new (buf+2*sizeof(T)) T
«поместить» ваши объекты в это пространство памяти.
Проблема в том, что в приведенном выше примере я полностью проигнорировал требования выравнивания, что опасно.
Таким образом, вместо того, чтобы реплицировать, какие пулы памяти и std::vector
делай, просто используй std::vector
!
std::vector<T> vec;
vec.reserve(end-begin);
for (unsigned int i = begin; i < end; ++i){
vec.push_back(std::move(array[i]);
}
и вы сделали.
Как указывают другие void
не имеет определенного размера, так void*
не может быть проиндексирован как массив.
Где же temp[1]
начать?
Наивное исправление:
T *temp = std::malloc((end-begin) * sizeof(T));
Так же плохо, когда используется в сочетании с:
temp[i] = std::move(array[i]);
Этот код будет вызывать оператор присваивания, и, если нетривиально, произойдет сбой при работе с мусором, который находится в temp[i]
, Например, если назначение явно или неявно отменяет выделение ресурсов в цели назначения, оно будет работать с неинициализированным temp[i]
и потерпеть неудачу.
Если (ЕСЛИ!) Вы сделали это, правильный код будет:
std::memcpy(temp+i*sizeof(i),array[I],sizeof(T));
Эта строка кода по существу предполагает, что T
является тривиально копируемым типом.
Первая строка предполагает наличие объектов T
есть тривиальный конструктор по умолчанию.
Если T
имеет тривиальный конструктор по умолчанию, большинство компиляторов будут оптимизировать инициализацию элемента (что, как я предполагаю, пытается избежать спрашивающий).
Так что рекомендуемый код (используя for
цикл) это:
T* temp= new T[end-being];
for(unsigned int i=begin;i<end;++i){
temp[i-begin]=array[i];
}
Примечание: этот код также исправляет ошибку в исходном коде, которая ломается, когда begin!=0
,
Код, похоже, копирует подраздел из середины array
к началу temp
но забывает начать с индекса 0
из temp
,
Но рекомендуемый подход C ++ к такому копированию:
T* temp= new T[end-being];
std::copy(array+begin,array+end,temp);
Хорошие реализации будут определять и использовать любые объемные операции с памятью, где это возможно.
Так что неявно это должно привести к std::memmove(temp,array,(end-begin)*sizeof(T));
если действительный.
Единственный последний поворот, который компилятор может не распознать, это то, что потенциально немного более эффективный
std::memcpy(temp,array,(end-begin)*sizeof(T));
На самом деле действительно в этом случае, потому что мы знаем temp
не может пересекаться с array
(не говоря уже о копируемом диапазоне).
Сноска. Как отмечалось в комментариях, самый обычный подход C ++ заключается в использовании std::vector
который обычно можно предположить, что он использует эти оптимизации за кулисами. Однако, если по какой-то причине реорганизация приложения невозможна или нежелательна, этот ответ предоставляет действительный эффективный способ переписать предоставленный код.
Пытаться
T *temp = (T*) malloc((end-begin) * sizeof(T));