C++ size_t и ptrdiff_t для индексации отрицательных массивов
Мне трудно выбирать между size_t
а также ptrdiff_t
для типа индекса, который должен иметь возможность хранить отрицательное значение.
Чтобы быть точным, в моем коде мне нужно реализовать массив. Я получаю его длину (в конструкторе) как тип size_t
и когда я перегружал [] operator
Мне нужно чтобы индекс был типа ptrdiff_t
(и не size_t
), так как я хочу разрешить отрицательные индексы, как показано в этом примере:
std::size_t length = 50;
MyVector<int> vec(length);
vec[0] = 10;
MyVector<int> vec2 = vec+1;
std::cout << vec2[-1] << std::endl; //should print 10
Проблема, возникающая в связи с указанным дизайном, заключается в том, что диапазон доступных индексов ограничен максимальным значением ptrdiff_t
и в некоторых машинах этот верхний предел меньше максимального значенияsize_t
,
т.е. std::numeric_limits<std::ptrdiff_t>::max() < std::numeric_limits<std::size_t>::max()
Таким образом, проблема заключается в том, что пользователь может создать массив с размером, превышающим максимальное значение ptrdiff_t
(но все еще в диапазоне size_t
, конечно), но он не сможет получить доступ к элементам массива, которые превышают максимальное значениеptrdiff_t
потому что их индексы будут переполнены до отрицательного числа. На моей машине это сокращает доступные индексы в два раза! (поскольку оба size_t
а также ptrdiff_t
64 бита, но один unsigned
а другой signed
)
Вот решения, которые я придумал, но, к сожалению, ни одно из них не является идеальным:
В конструкторе примите длину типа
ptrdiff_t
вместоsize_t
и добавьте проверку, которая проверяет, что данная длина не является отрицательной.Плюсы: это решает проблему, так как теперь я мог бы получить доступ ко всем элементам в массиве, и все же учесть отрицательные индексы.
Минусы: ограничивает максимально возможную длину массива. (например, как я уже говорил ранее, в моей машине он сокращается вдвое)
Оставьте вещи такими, какие они есть, но в
[] operator
приведите указатель к типуsize_t
и использовать тот факт, что отрицательное значение будет переполнено.то есть к данному индексу, добавьте разницу между элементом, на который мы сейчас указываем, и
например, в моем примере ранее, поскольку vec2 указывает на второй элемент в
[] operator
будет выглядеть примерно такtemplate<class T> T& MyVector<T>::operator[] (std::ptrdiff_t index) { //Since vec2 points to the second element, we add 1. //For vec, we would add 0 since it points at the //first element in the array. std::size_t actual_index = static_cast<std::size_t>(index + 1); //Do boundary checking return this->ptr[actual_index]; }
Плюсы: теперь мы можем получить доступ ко всем элементам массива.
Минусы: использование становится неуклюжим и уродливым. Например, если мы создадим вектор размером
std::numeric_limits<std::size_t>::max()
затем, чтобы получить доступ к последнему элементу, нам нужно получить доступ к элементу '-1':MyVector<int> big_vector(std::numeric_limits<std::size_t>::max()); big_vector[-1] = 5; // big_vector[-1] is the last element in the array. MyVector<int> big_vector2 = big_vector + 1; std::cout << big_vector2[-2] << std::endl; // would print 5, since big_vector2 points at the second element in the array
1 ответ
У меня возникают трудности с выбором между size_t и ptrdiff_t для типа индекса, который должен быть в состоянии хранить отрицательное значение.
Почему вы ограничиваете себя size_t и ptrdiff_t? А как насчет других целочисленных типов?
Есть ли у вас реальный план по внедрению контейнера с элементами 4.294.967.296? (то есть 2 ^ 32
).
Я не думаю, что сегодня существует платформа, которая будет выделять столько памяти - по крайней мере, не на компьютерах потребительского уровня. Вы работаете на специализированном оборудовании?
Для универсального C++ вы можете написать std::size_t
- принятие конструктора к std::ptrdiff_t
Индексированный класс.
Возможно, вам придется настроить допустимый диапазон индексов, так что если вы принимаете отрицательные индексы, эффективный диапазон индексов должен быть (-max32bit, max32bit).
Это означает, что если вы пытаетесь использовать класс с индексом в диапазоне (max32bit, max64bit), ваш класс должен выдать исключение.
Мое решение будет принимать std::size_t
в конструкторе (с четкой документацией о том, что индекс варьируется от -max (ptrdiff_t
) до макс (ptrdiff_t
), а не от 0 до max (std::size_t
). Затем класс будет проиндексирован с использованием std::ptrdiff_t
,