Можно ли в C++ 17 изменять глобальные переменные в функции constexpr?
#include <iostream>
int global = 0;
constexpr int Foo(bool arg) {
if (arg) {
return 1;
}
return global++;
}
int main() {
std::cout << global;
Foo(true);
std::cout << global;
Foo(false);
std::cout << global;
}
Я бы не ожидал, что вы сможете, но clang 6 позволяет: https://godbolt.org/g/UB8iK2
GCC, однако, не делает: https://godbolt.org/g/ykAJMA
Какой компилятор правильный?
Ни один из них не сработает, если вы принудительно скомпилируете оценку времени компиляции constexpr int a = Foo(false), но я не уверен, что это неверно.
Мне это просто кажется основной ошибкой в gcc
Определенно ошибка gcc, отправлено 86327
Интересно отметить, что clang не позволяет оценивать функцию как постоянное выражение в ветке, которая модифицирует global (как отмечено @ user975989), и если вы удалите ветвь if (arg) return 1;, она вообще не сможет скомпилироваться. , несмотря на то, что он по-прежнему соответствует требованиям, перечисленным в ответе @codekaiser.
@JohnIlacqua: По крайней мере, первая часть совсем не удивительна, потому что разрешение этого нарушило бы пункт маркера «модификация объекта» в [expr.const].





Which compiler is correct?
Кланг прав.
Определение функции constexpr согласно dcl.constexpr / 3
The definition of a
constexprfunction shall satisfy the following requirements:
(3.1) its return type shall be a literal type;
(3.2) each of its parameter types shall be a literal type;
(3.3) its function-body shall be= delete,= default, or a compound-statement that does not contain:
(3.3.1) an asm-definition,
(3.3.2) a goto statement,
(3.3.3) an identifier label,
(3.3.4) a try-block, or
(3.3.5) a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.
Также согласно dcl.constexpr / 5:
For a
constexprfunction or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression,
Foo(true) можно оценить как постоянное выражение ядра (то есть 1) .
Кроме того, Foo(false)может быть, но не требуется, чтобы он оценивался постоянно.
ЗАКЛЮЧЕНИЕ
Таким образом, ошибка в GCC.
Это определенно яснее. Я считаю, что компилятор фактически не может рассуждать о значении global, даже если он встраивает Foo(false) в main, потому что global имеет внешнюю связь. (Выходной сигнал не обязательно должен быть 0\n0\n1, потому что некоторый глобальный конструктор в другом модуле компиляции может изменить global) Следовательно, его побочные эффекты времени выполнения должны быть сохранены.
Хотя я покупаю ваше объяснение, на самом деле это может быть дефект или, по крайней мере, двусмысленность стандарта, на который попались ребята из GCC. dcl.constexpr / 7 указывает "... тот же результат, что и non-constexpr ... за исключением того, что может появиться в константном выражении". Итак, Foo(false), очевидно, не могу фигурирует в постоянном выражении, так что ... может быть, поэтому.
Не могли бы вы добавить какое-нибудь объяснение того, что должно произойти для примера OP, нестандартной тарабарщиной? Я склонен сказать, что у нас здесь, прежде всего, ошибка в спецификации, но не для того, чтобы сделать это незаконным. Но, возможно, я просто недостаточно хорошо понял constexpr.
@leftaroundabout, попробую. global может или не может быть изменен в других единицах трансляции из-за его связи, и он явно не определен как constпрограммистом. Если компилятор может оценить выражение во время компиляции, он это сделает; но не значит, что это должно. Таким образом, на мой взгляд, лучше иметь оговорку без ограничений (можно, но не обязательно). См. Также комментарий Бена Фойгта.
@Damon, правда Foo(false)не может появиться в константном выражении. Поддерживая ваше утверждение, в приведенном коде это просто вызов функции (а не постоянное выражение). В отличие от constexpr int i = Foo(false);, это вызовет диагностическое сообщение с помощью Clang.
Добавлю, что dcl.constexpr / 5 дополнительно требует:
For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, a constant initializer for some object ([basic.start.static]), the program is ill-formed, no diagnostic required.
Поскольку вы намеренно написали функцию так, чтобы Foo(true) вычислял основное постоянное выражение, Foo(false) не требуется.
@AnT • Мне нужна шпаргалка, чтобы четко соблюдать ограничения C++ 11, C++ 14 и C++ 17.