Неопределенное поведение сдвига вправо в С++

От cppreference.com:

Для unsigned a и для подписанного a с неотрицательными значениями значение a >> b является целой частью a/2 b. Для отрицательного a значение a >> b определяется реализацией (в большинстве реализаций это выполняет арифметический сдвиг вправо, так что результат остается отрицательным).

В любом случае, если значение правильного операнда отрицательное или больше или равно числу бит в продвинутом левом операнде, поведение не определено.

Почему у нас есть неопределенное поведение в случае, если правый операнд больше или равен количеству бит в продвинутом левом операнде?
Мне кажется, что результат должен быть 0 (по крайней мере для неподписанных/положительных целых чисел)...

В частности, с g++ (версия 4.8.4, Ubuntu):

unsigned int x = 1;
cout << (x >> 16 >> 16) << " " << (x >> 32) << endl;

дает: 0 1

Ответ 1

Одна из целей C++ - обеспечить быстрый и эффективный код "близко к оборудованию". И на большинстве аппаратных средств целочисленный сдвиг вправо или сдвиг влево может быть реализован одним операционным кодом. Проблема в том, что разные процессоры имеют разное поведение в этом случае, когда величина сдвига больше, чем количество бит.

Поэтому, если C++ обязал особое поведение для операций сдвига, при создании кода для процессора, поведение кода операции которого не соответствует всем требованиям стандарта, компиляторам необходимо будет вставить проверки и логику, чтобы убедиться, что результат соответствует стандарту во всех случаях. Это должно произойти почти во всех случаях использования встроенных операторов сдвига, если оптимизатор не может доказать, что угловой случай на самом деле не произойдет. Добавленные проверки и логика потенциально замедляют работу программы.

Ответ 2

Чтобы дать конкретный пример, x86 выравнивает счетчик сдвига до 5 бит (6 бит для 64-битных сдвигов), в то время как ARM уменьшает счетчик сдвига до 8 бит. При использовании текущего стандарта C++ компиляторы для обоих процессоров могут реализовать сдвиги с помощью одного кода операции.

Если стандарту C++ следует определить результат сдвигов более чем на длину операнда определенным образом, компиляторы, нацеленные, по крайней мере, на одно из семейств ЦП (и, возможно, оба варианта, если результат, требуемый C++, не будет соответствовать либо аппаратная реализация, как и поведение, которое вы предлагаете) должны были бы реализовать каждую операцию переключения с помощью ветвей, которые приведут к требуемому результату, где код операции CPU не будет.