Мне трудно выбирать между 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
У меня возникают трудности с выбором между 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
,