std::end для unique_ptr

#c #c 11

#c #c 11

Вопрос:

Я хочу реализовать std::end для уникального указателя. Проблема в том, что я должен получить N (количество элементов в массиве).

1.Подход к выводу типа из шаблона

 template <typename T, size_t N>
T* end(const unique_ptr<T[N]> amp;arr)
{
    return arr.get()   N;
}
  

Но я получил ошибку ошибка: C2893: Не удалось специализировать шаблон функции ‘T * test::end(const std::unique_ptr> amp;)’ с [_Ty=T [N] ] Со следующими аргументами шаблона: ‘T= int’ ‘N = 0x00’

Похоже, что невозможно вывести N

2.Получаем N из распределителя. Распределитель должен знать N, чтобы правильно выполнить delete[] . Вы могли бы прочитать об этом в этой статье. Существует два подхода:

  1. Чрезмерно выделите массив и поместите n чуть левее.

  2. Используйте ассоциативный массив с p в качестве ключа и n в качестве значения.

Проблема в том, как получить кроссплатформенный / компилятор такого размера.

Может быть, кто-то знает лучшие подходы или знает, как заставить это работать?

Комментарии:

1. Этот код действительно работает … если вам действительно нужно unique_ptr<T[N]> передать в end , а не в unique_ptr<T[]> . Возможно, в вашем вопросе вы могли бы также показать какой-то такой же код, показывающий, как вы собираетесь вызывать end

Ответ №1:

Если у вас есть массив размером с время выполнения, и вам нужно знать его размер без необходимости вручную вести бухгалтерский учет, тогда вам следует использовать std::vector . Он будет управлять памятью и размером для вас.

std::unique_ptr<T[]> это просто оболочка для необработанного указателя. Вы не можете получить размер блока, на который указывает указатель, только из указателя. Причина, по которой вы используете std::unique_ptr<T[]> over T* foo = new T[size] , заключается в том, что функция unique_ptr гарантирует delete[] , что вызывается, когда указатель выходит за пределы области видимости.

Ответ №2:

Что-то вроде этого?

 template<class X>
struct sized_unique_buffer;

template<class T, std::size_t N>
struct sized_unique_buffer<T[N]>:
  std::unique_ptr<T[]>
{
  using std::unique_ptr<T[]>::unique_ptr;
  T* begin() const { return this->get(); }
  T* end() const { return *this?begin(*this) N:nullptr; }
  bool empty() const { return N==0 || !*this; }
};
  

где мы имеем неисполненное обещание фиксированной продолжительности времени компиляции во время компиляции.

Подобный дизайн мог бы работать для динамической продолжительности выполнения.

В некоторых компиляторах количество, T когда T можно тривиально уничтожить, не сохраняется при вызове new T[N] . Система вольна перераспределять и предоставлять вам буфер большего размера (т. Е. округлять до границы страницы для большого выделения или неявно сохранять размер буфера через местоположение, из которого он выделен, чтобы уменьшить накладные расходы и округлить выделения в большую сторону), поэтому размер выделения не обязательно должен точно соответствовать количеству элементов.

Для нетривиально уничтоженных T верно, что компилятор должен знать, сколько нужно уничтожить, исходя только из указателя. Эта информация не предоставляется C .

Вы можете выполнить ручное распределение буферов и подсчета и передать это в unique_ptr с пользовательским удалителем, даже без состояния. Это позволило бы ввести тип

 unique_buffer<T[]> ptr;
  

где вы можете получить количество элементов при незначительных затратах времени выполнения.

Если вы вместо этого сохраните длину в deleter, вы можете получить немного больше локальности в пределах цикла (экономя пропуск кэша) за счет большего unique_buffer<T[]> .

Выполнение этого с помощью unadulterated unique_ptr<T[]> невозможно переносимым способом.