Подтвердить что ты не робот

Может ли constexpr сочетаться с изменчивым?

Следующий фрагмент отлично работает в Clang 3.5, но не в GCC 4.9.2:

int main()
{
    constexpr volatile int i = 5;
}

с ошибкой:

Ошибка: как "volatile", так и "constexpr" не могут использоваться здесь

Если я проверяю сборку, которую создает Clang, она показывает 5, как ожидалось:

movl    $5, -4(%rsp)

В GCC constexpr int i = 5 оптимизируется, но volatile int i = 5 также показывает 5 в сборке. volatile const int i = 5 компилируется в обоих компиляторах. Это не внешняя концепция для того, чтобы что-то было одновременно изменчивым и постоянным.

Какой компилятор корректен по стандартам?

4b9b3361

Ответ 1

Да, это действительно так, был отчет о дефекте 1688: переменные volatile constexpr, которые были поданы для этого, говоря:

В текущей формулировке не указывается язык что constexpr не может применяться к переменной нестабильной квалификации тип. Кроме того, формулировка в пункте 5.19 [expr.const], относящаяся к "энергонезависимый объект, определенный с помощью constexpr", может привести к выводу что комбинация разрешена, но такая переменная не может появляются в постоянном выражении. Каковы намерения?

он был отклонен как не дефект (NAD), ответ и обоснование:

Комбинация намеренно разрешена и может использоваться в некоторых обстоятельства для принудительной инициализации.

Как указывает DR, такая переменная сама не может использоваться в постоянном выражении:

constexpr volatile int i = 5;    
constexpr int y = i ;         // Not valid since i is volatile

Раздел [expr.const]/2 включает в себя все случаи, которые делают условное выражение не ключевым константным выражением, включая:

преобразование lvalue-to-rvalue (4.1), если оно не применяется к

и все исключения требуют:

[...], который относится к энергонезависимому объекту [...] [...]

Ответ 2

Цитата N4140 [dcl.constexpr]/9:

A constexpr спецификатор, используемый в объявлении объекта, объявляет объект как const. Такой объект должен иметь буквальный тип и должен быть инициализирован.

Литеральный тип определен в [basic.types]/10:

Тип - это буквальный тип, если он:

(10.1) - void; или

(10.2) - скалярный тип; или

(10.3) - ссылочный тип; или

(10.4) - массив буквального типа; или

(10.5) - тип класса (раздел 9), который имеет все следующие свойства:

(10.5.1) - он имеет тривиальный деструктор,

(10.5.2) - это совокупный тип (8.5.1) или имеет хотя бы один конструктор конструктора или конструктора constexpr, который не является конструктором копирования или перемещения, и

(10.5.3) - все его нестатические элементы данных и базовые классы являются нелетучими литералами.

Скалярный тип находится в пункте 9:

Арифметические типы (3.9.1), типы перечислений, типы указателей, указатели на типы членов (3.9.2), std::nullptr_t и cv-квалификационные версии этих типов (3.9.3) совокупно называются скалярными типами.

int является арифметическим, поэтому volatile int является скалярным типом и, следовательно, типом литерала. constexpr volatile int i = 5; является, таким образом, хорошо сформированным объявлением.

Интересно, что выражение, которое оценивает i, не может быть выражением ядра-константы, поскольку оно применяет преобразование lvalue-to-rval в значение gl volatile type ([expr.const]/2). Следовательно, выражения, которые оценивают i, не являются интегральными константными выражениями или постоянными выражениями. Я не уверен, что constexpr в этом объявлении имеет какой-либо эффект, кроме i неявно const и (nod to @TC), требующий его инициализатора быть постоянным выражением.

Я сообщил об этом как ошибка GCC 65327, мы увидим, что люди GCC должны сказать.

2015-03-16 Обновление: ошибка была исправлена ​​для GCC 5.