Почему выражение i = ++i + i++ допустимо в constexpr функции?
На cppreference столкнулся с утверждением, что i = ++i + i++ это ub, вспомнив, что на этапе компиляции вроде как не может быть выполнен код с ub решил проверить накидав несложный код:
#include <iostream>
constexpr int func(int i){
i = ++i + i++;
return i;
}
constexpr int glob_i = func(1);
int main()
{
std::cout << glob_i << "\n"; // выводит 4
return 0;
}
Никак не могу разобраться, почему это не приводит к ошибке компиляции? И если уж это компилируется, почему аналогичные вычисления в рантайме дают результат 5?
Пример на godbolt: https://godbolt.org/z/rM8b7szz5
Статья на cppreference откуда все началось https://en.cppreference.com/w/cpp/language/eval_order
Ответы (1 шт):
Согласно [expr.const] §10.8 если выражение вызывает НП (неопределенное поведение), то оно перестает быть constexpr выражением:
(10) Выражение E является основным постоянным выражением, если при оценке E, следуя правилам абстрактной машины ([intro.execution]), не будет получено одно из следующих:
...
(10.8) — Операция, которая привела бы к неопределенному или ошибочному поведению, как указано в [intro] до [cpp].
А также в [intro.defs] §3.65 говорится что:
Оценка постоянного выражения ([expr.const]) никогда не приводит к поведению, явно указанному как неопределенное в [intro] до [cpp].
Т.е. тот факт что ваша constexpr функция вызывает НП, не означает что это должно привести к ошибке компиляции per se. Она просто перестанет быть constexpr (т.е. будет работать только в runtime).
Но, если вы попытаетесь присвоить значение этой функции constexpr переменной, то это уже должно вызвать ошибку компиляции. Например:
constexpr auto f(const int* p) { return *p; }
int main()
{
auto a = f(nullptr); // OK, but runtime UB
constexpr auto b = f(nullptr); // ERROR
return a + b;
}
Как мы видим все 3 главных компилятора выдают ошибку: https://godbolt.org/z/4bzd8YvvG
Но компилятор не всегда может достоверно определить, является ли поведение функции НП. Я подозреваю это и происходит в вашем случае. Возьмем упрощенную версию:
constexpr auto f(int i)
{
i = ++i + i++;
return i;
}
int main()
{
auto a = f(42); // OK, but runtime UB
constexpr auto b = f(42); // should be ERROR
return a + b;
}
Как ни странно все 3 компилятора работают: https://godbolt.org/z/Ta8j6Tsz9
То есть, в первом примере компилятор может точно определить НП, а во втором нет. Поэтому gcc и clang просто выдают диагностическое сообщение, а msvc вообще молчит...