@SergejEU, чтож, продолжим.
Во-первых, я должен отметить, что вы пытаетесь выйти из контекста изначальной задачи. Ещё раз уточню, необходимо было реализовать
operrator[]
, для итерирования по массиву, который является инвариантом класса. Т.е. обращение к массиву будет не прямое, а через метод. По контексту точно известно (так как этот массив создается и уничтожается внутри класса), итерироваться нужно только относительно 0-го элемента и только вверх, значит отрицательные значения не валидны, нужны только положительные, поэтому логично сделать сигнатуру с приемом в качестве аргумента беззнакового целого. Таким образом внутри функции
operrator[]
всегда будет положительное целое, даже, если в программе будут передавать отрицательное число, сигнатура функции не может поменяться в процессе выполнения, если конечно не будет соответствующей перегрузки, но такую перегрузку нет смысла делать, и в примере её нет.
Во-вторых, ссылка на чужой проект - это ссылка на исходник реализации стандартной библиотеки компилятора GCC. ТС сам написал, что, в учебных целях, хотел сделать свою реализацию, чтобы лучше разобраться в нюансах, поэтому ссылка на референс, я считаю, абсолютно уместна.
Теперь давайте разберем ваш контрпример, к которому сразу несколько замечаний:
1) Почему для переменной
index
в качестве типа используется
long int
, вы же за
ptrdiff_t
рассуждали все это время. И это имеет значение при переносе кода на другую платформу, об этом будет ещё сказано ниже.
2) Значения для
index
и
number
выбраны не удачные, ниже увидите почему.
Прежде всего обратимся
к стандарту 7.4 Usual arithmetic conversions, на который я буду ссылаться.
Также я предлагаю добавить в ваш пример ещё проверку на равенство, посмотреть как теперь работает код на 64-разрядной платформе можно
тут
// requires 64-bit platforms, GNU GCC
#include <iostream>
#include <cstdlib>
int main( void ) {
long int index = -4294967296;
size_t number = 18446744069414584320UL;
std::cout << "number = " << number << std::endl;
std::cout << "index = " << index << std::endl;
if ( number < index ) {
std::cout << "Your prediction is ok" << std::endl;
} else if (number == index) {
std::cout << "Yep, these numbers are equal;" << std::endl;
} else {
std::cout << "go learn your lessons!" << std::endl;
}
return 0;
}
Попробуем запустить, и увидим, что выполнится условие
else if (number == index)
. Почему так?
Конкретно для той платформы, которая выполняет этот код (см ссылку),
conversion rank
для типов
long int
и
size_t
(
size_t
в данном случае alias для
unsigned long
) одинаковый, что на это говорит стандарт:
if the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type shall be converted to the type of the operand with unsigned integer type.
Не буду переводить стандарт, но попрошу обратить внимание, на то, что в конкретном случае (конкретная платформа на которой запускался пример) представление
index
и
number
в памяти идентичное. Собственно поэтому они и равны, и ровно по этому неважно к какому типу будет приведение
long int
или
size_t
, кроме того, на платформах с представлением данных типов меньше 64бит, все эти значения будут срезаться до 0. И также будет выполняться условие
else if (number == index)
. Поэтому в том виде котором пример существует сейчас - забавен, но бесполезен, так как ничего конкретного не доказывает.
Я предлагаю изменить эти значения, на более интересные,
ссылка на исполняемую платформу
// requires 64-bit platforms, GNU GCC
#include <iostream>
#include <cstdlib>
int main( void ) {
long int index = -1;
size_t number = 65520UL; /* 65520 = 0xFFF0 */
std::cout << "number = " << number << std::endl;
std::cout << "index = " << index << std::endl;
if ( number < index ) {
std::cout << "Your prediction is ok" << std::endl;
} else if (number == index) {
std::cout << "Yep, these numbers are equal;" << std::endl;
} else {
std::cout << "go learn your lessons!" << std::endl;
}
return 0;
}
Теперь мы видим, что выполняется условие
if ( number < index )
. Почему так - потому что бинарное представление -1 на данной платформе соответствует
0xFFFFFFFFFFFFFFFF
, а
0xFFFFFFFFFFFFFFFF
явно больше чем
0xFFF0
.
Таким образом уже точно можно сказать, выполняется пункт стандарта, который процитирован выше.
А теперь запустим этот код на другой платформе, на AVR. На AVR size_t - это uint16_t, а long int - int32_t, результат уже будет иной.
В стандарте такое результат объясняется следующим образом:
if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, the operand with unsigned integer type shall be converted to the type of the operand with signed integer type.
Т.е.
size_t
был приведен к
long int
, но при этом осталось положительным, а
index
- отрицательное число, положительное число больше отрицательного - ожидаемый результат.
Разные результаты на разных платформах - это плохо, это значит код имеет плохую переносимость, и ваш пример, слегка доработанный - это очень явно показывает. Т.е. переносимый код - код который будет работать одинаково на разных платформах, и если long int заменить на ptrdiff_t, то, внезапно, примеры заработают одинаково.
Ничего в том зазорного нет, все мы когда-то заблуждаемся, но упорствовать и не признавать своих ошибок это уже перебор
Вот тут я с вам полностью согласен.