Возьми следующий код
#include <iostream>
void func() {
int i = 2147483640;
while (i < i + 1)
{
std::cerr << i << '\n';
++i;
}
return;
}
int main() {
func();
}
Этот код явно неверен, так как цикл while может завершиться только в случае переполнения подписанного int i
, то есть UB, и, следовательно, компилятор может, например, оптимизировать его в бесконечный цикл (что Clang делает на -O3
) или выполнить другое всякие прикольные вещи. Теперь мой вопрос: из моего прочтения стандарта C++ типы, эквивалентные до подписи, могут иметь псевдоним (то есть указатели int*
и unsigned*
могут иметь псевдоним). Для того, чтобы сделать некоторую фанки со знаком "обертывание", имеет ли следующее неопределенное поведение или нет?
#include <iostream>
static int safe_inc(int a)
{
++reinterpret_cast<unsigned&>(a);
return a;
}
void func() {
int i = 2147483640;
while (i < safe_inc(i))
{
std::cerr << i << '\n';
++i;
}
return;
}
int main() {
func();
}
Я пробовал приведенный выше код с Clang 8 и GCC 9 на -O3
с -Wall -Wextra -Wpedantic -O3 -fsanitize=address,undefined
аргументы и не получаю ошибок или предупреждений, и цикл завершается после переноса в INT_MIN
.
cppreference.com говорит мне, что
Тип псевдонимов
Всякий раз, когда делается попытка прочитать или изменить сохраненное значение объекта типа DynamicType с помощью glvalue типа AliasedType, поведение не определено, если не выполнено одно из следующих условий:
- AliasedType является (возможно, cv-квалифицированным) подписанным или неподписанным вариантом DynamicType.
что из моего прочтения означает, что для целей псевдонимов типов подпись не учитывается, и код, использующий reinterpret_cast
имеет четко определенную семантику (хотя, в любом случае, он немного глуповат).