Статический констекстер одр-используется или нет?
Почему это работает на gcc
но не на clang
( посмотреть его вживую):
constexpr int giveMeValue() { return 42; }
struct TryMe {
static constexpr int arr[1] = {
giveMeValue()
};
};
int main() {
int val = TryMe::arr[0];
return val;
}
Я получаю неразрешенный внешний символ с лязгом.
Является TryMe::arr[0]
объект? Если да, то используется ли он?
1 ответ
TryMe::arr
используется odr, но вы не даете определение ( смотрите его вживую):
constexpr int TryMe::arr[1];
Почему результат несовместим между gcc
а также clang
? Это связано с тем, что нарушения odr не требуют дезагностики как из проекта стандарта C++11, так и из C++14 (выделено мое):
Каждая программа должна содержать ровно одно определение каждой не встроенной функции или переменной, которая используется в этой программе в виде odr; Диагностика не требуется.
Мы видим, что он используется в проекте стандарта C++11, раздел 3.2
который говорит:
Выражение потенциально оценивается, если оно не является неоцененным операндом (раздел 5) или его подвыражением. Переменная, имя которой появляется в качестве потенциально оцениваемого выражения, используется odr, если только она не является объектом, удовлетворяющим требованиям для появления в константном выражении (5.19), и преобразование lvalue-в-значение (4.1) применяется немедленно.
TryMe::arr
является объектом, и он удовлетворяет требованиям для появления в константном выражении, но преобразование lvalue в rvalue не применяется немедленно к TryMe::arr
но TryMe::arr[0]
,
Обновленная формулировка из проекта стандарта C++14, которая также применяется к C++11, поскольку она была применена в отчете о дефектах ( DR 712):
Переменная x, имя которой появляется в качестве потенциально оцениваемого выражения ex, используется odr, если только при применении преобразования lvalue-to-rvalue (4.1) в x не получается константное выражение (5.19), которое не вызывает никаких нетривиальных функций и, если x является объектом, ex является элементом набора потенциальных результатов выражения e, где к e применяется либо преобразование lvalue-в-значение (4.1), либо e является выражением отброшенного значения
Потенциальные результаты выражения TryMe::arr[0]
пуст по критериям в 3.2
параграф 2
и поэтому он используется.
Примечание: вы должны предоставить определение вне класса согласно разделу 9.4.2
[class.static.data], который говорит (выделение мое):
Статический член данных литерального типа может быть объявлен в определении класса с помощью спецификатора constexpr; если это так, его объявление должно указывать инициализатор скобок или равных, в котором каждое предложение инициализатора, являющееся выражением присваивания, является константным выражением. [Примечание: в обоих этих случаях член может появляться в константных выражениях. - примечание] Элемент все еще должен быть определен в области пространства имен, если он используется в программе с помощью odr (3.2), и определение области пространства имен не должно содержать инициализатор
Обновить
TC указал на отчет о дефектах 1926 года, в котором добавлен следующий пункт 3.2
[basic.def.odr] пункт 2:
- Если e - операция подписки (5.2.1 [expr.sub]) с операндом массива, набор содержит этот операнд.
Это означает, что подписка на массив больше не является использованием odr, и поэтому код OPs будет правильно сформирован в C++1z и выглядит как C++14, поскольку дефект выглядит так, как если бы он был против C++14.