Изменение глобальной переменной в функции constexpr в C ++ 17 - PullRequest
0 голосов
/ 27 июня 2018

В 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

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

Ответы [ 2 ]

0 голосов
/ 27 июня 2018

Я добавлю, что dcl.constexpr / 5 дополнительно требует:

Для функции constexpr или конструктора constexpr, который не является ни значением по умолчанию, ни шаблоном, если не существует значений аргумента, так что вызов функции или конструктора может быть оцененным подвыражением выражения основной константы или, для конструктора, постоянный инициализатор для некоторого объекта ([basic.start.static]), программа некорректна, диагностика не требуется.

Поскольку вы неумышленно написали функцию так, чтобы Foo(true) вычислялось как выражение константы ядра, Foo(false) не требуется.

0 голосов
/ 27 июня 2018

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

Clang прав.

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

Определение функции constexpr должно удовлетворять следующему Требования:

(3.1) его возвращаемый тип должен быть литеральным типом;
(3.2) каждый из его типов параметров должен быть литеральным типом;
(3.3) его тело функции должно быть = delete, = default или составной оператор не содержит:

(3.3.1) определение asm,
(3.3.2) оператор goto,
(3.3.3) метка идентификатора,
(3.3.4) try-block или
(3.3.5) определение переменной не буквального типа или static или продолжительность хранения потока или для которой не выполняется инициализация.

Также согласно dcl.constexpr / 5 :

Для constexpr функции или конструктора constexpr, который не является ни по умолчанию ни шаблон, если не существует значений аргумента , таких как вызов функции или конструктора может быть оцененным подвыражение основного константного выражения,

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

Кроме того, Foo(false) может быть , но не обязательно для постоянной оценки.

ЗАКЛЮЧЕНИЕ

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


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