Изменение глобальной переменной в функции constexpr в C++ 17

Можно ли в 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

Какой компилятор правильный?

@AnT • Мне нужна шпаргалка, чтобы четко соблюдать ограничения C++ 11, C++ 14 и C++ 17.

Eljay 27.06.2018 03:28

Ни один из них не сработает, если вы принудительно скомпилируете оценку времени компиляции constexpr int a = Foo(false), но я не уверен, что это неверно.

user975989 27.06.2018 03:30

Мне это просто кажется основной ошибкой в ​​gcc

M.M 27.06.2018 03:43

Определенно ошибка gcc, отправлено 86327

Barry 27.06.2018 03:47

Интересно отметить, что clang не позволяет оценивать функцию как постоянное выражение в ветке, которая модифицирует global (как отмечено @ user975989), и если вы удалите ветвь if (arg) return 1;, она вообще не сможет скомпилироваться. , несмотря на то, что он по-прежнему соответствует требованиям, перечисленным в ответе @codekaiser.

John Ilacqua 27.06.2018 04:03

@JohnIlacqua: По крайней мере, первая часть совсем не удивительна, потому что разрешение этого нарушило бы пункт маркера «модификация объекта» в [expr.const].

Damon 27.06.2018 11:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
36
6
2 085
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Which compiler is correct?

Кланг прав.

Определение функции constexpr согласно dcl.constexpr / 3

The definition of a constexpr function 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 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,

Foo(true) можно оценить как постоянное выражение ядра (то есть 1) .

Кроме того, Foo(false)может быть, но не требуется, чтобы он оценивался постоянно.

ЗАКЛЮЧЕНИЕ

Таким образом, ошибка в GCC.


Большое спасибо @Barry, @aschepler и @BenVoigt за помощь с этим ответом.

Это определенно яснее. Я считаю, что компилятор фактически не может рассуждать о значении global, даже если он встраивает Foo(false) в main, потому что global имеет внешнюю связь. (Выходной сигнал не обязательно должен быть 0\n0\n1, потому что некоторый глобальный конструктор в другом модуле компиляции может изменить global) Следовательно, его побочные эффекты времени выполнения должны быть сохранены.

Ben Voigt 27.06.2018 06:36

Хотя я покупаю ваше объяснение, на самом деле это может быть дефект или, по крайней мере, двусмысленность стандарта, на который попались ребята из GCC. dcl.constexpr / 7 указывает "... тот же результат, что и non-constexpr ... за исключением того, что может появиться в константном выражении". Итак, Foo(false), очевидно, не могу фигурирует в постоянном выражении, так что ... может быть, поэтому.

Damon 27.06.2018 12:02

Не могли бы вы добавить какое-нибудь объяснение того, что должно произойти для примера OP, нестандартной тарабарщиной? Я склонен сказать, что у нас здесь, прежде всего, ошибка в спецификации, но не для того, чтобы сделать это незаконным. Но, возможно, я просто недостаточно хорошо понял constexpr.

leftaroundabout 27.06.2018 12:42

@leftaroundabout, попробую. global может или не может быть изменен в других единицах трансляции из-за его связи, и он явно не определен как constпрограммистом. Если компилятор может оценить выражение во время компиляции, он это сделает; но не значит, что это должно. Таким образом, на мой взгляд, лучше иметь оговорку без ограничений (можно, но не обязательно). См. Также комментарий Бена Фойгта.

Joseph D. 28.06.2018 02:44

@Damon, правда Foo(false)не может появиться в константном выражении. Поддерживая ваше утверждение, в приведенном коде это просто вызов функции (а не постоянное выражение). В отличие от constexpr int i = Foo(false);, это вызовет диагностическое сообщение с помощью Clang.

Joseph D. 28.06.2018 03:18

Добавлю, что 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) не требуется.

Другие вопросы по теме