Следующая программа на C ++ 11:
int x = 42;
void f()
{
int y = 43;
static_assert(&x < &y, "foo");
}
int main()
{
f();
}
Не компилируется с gcc 4.7, так как жалуется:
error: ‘&y’ is not a constant expression
Это согласуется с моей интуицией. Адрес y
потенциально меняется с каждым вызовом f
поэтому, конечно, он не может быть рассчитан во время перевода.
Однако ни один из пунктов маркера в 5.19 [expr.const], кажется, не препятствует тому, чтобы это было постоянным выражением.
Я вижу только двух претендентов:
преобразование lvalue в rvalue …
но если я не ошибаюсь (?), в программе нет преобразований lvalue в rvalue.
А также
id-expression
это относится к переменной [snip], если:
- инициализируется постоянным выражением
который y
is — инициализируется константным выражением 43
,
Так это ошибка в стандарте, или я что-то упустил?
Обновить:
Это чертовски запутанно, но я думаю, что я на вершине, поэтому позвольте мне показать пример, который продемонстрирует, что происходит:
int x = 42;
void f()
{
int y = 43;
// address constant expressions:
constexpr int* px = &x; // OK
constexpr int* py = &y; // ERROR: pointer context for local variable
// boolean constant expressions:
constexpr bool bx = &x; // OK
constexpr bool by = &y; // OK
// comparison constant expressions:
constexpr bool eq = (&x == &y); // OK
constexpr bool lt = (&x < &y); // ERROR: undefined behaviour disqualifies
a constant expression
}
int main()
{
f();
}
Сначала различайте основное постоянное выражение (5.19p2) и постоянное выражение (5.19p4). В частности, подвыражения постоянного выражения должны быть только основными постоянными выражениями, а не постоянными выражениями. То есть постоянное выражение является свойством полного выражения, а не подвыражений. Кроме того, необходимо посмотреть на контекст, в котором используется полное выражение.
Итак, как оказалось, ошибка GCC вводит в заблуждение. во-первых &y
может быть постоянным выражением в некоторых контекстах. Во-вторых, причина &x < &y
не является константным выражением из-за сравнения не связанных между собой указателей, а не подвыражения &y
,
Давайте попробуем определить, какие требования выражают в static_assert декларация должен выполнить пошагово, используя n3485.
[Dcl.dcl] / 1[Dcl.dcl] / 4static_assert-заявление:
static_assert (
константа-выражение,
Строка литерала) ;
[Expr.const] / 4В static_assert декларация константа-выражение должно быть константным выражением, которое может быть контекстно преобразовано в
bool
,
Коллективно, константные выражения, выражения константы, а также адресные постоянные выражения называются константные выражения.
Так, что тип константного выражения &x < &y
? это не адресное выражение:
адресное выражение является постоянным выражением ядра (после преобразований, как того требует контекст) типа
std::nullptr_t
или типа указателя […].
Тип &x < &y
является bool
согласно [expr.rel] / 1.
Это не выражение константы либо, так что это должно быть константное буквальное выражение, если есть.
константное буквальное выражение является основным значением постоянной константы литерального типа […]
Следовательно, &x < &y
должен соответствовать требованиям выражение основной константы.
Как указал TemplateRex а также HVD в комментариях, в данном конкретном случае, &x < &y
не соответствует требованиям выражение основной константы:
[выражение основной константы не должно содержать] оператор отношения или равенства, где результат не определен;[Expr.rel] / 2
Если два указателя
p
а такжеq
одного и того же типа указывают на разные объекты, которые не являются членами одного и того же объекта или элементов одного и того же массива или на разные функции, или, если только один из них равен нулю, результатыp<q
,p>q
,p<=q
, а такжеp>=q
не определены.
Тем не менее, для примера, как
int arr[2] = {1, 2};
static_assert(&a[0] < &a[1], "");
Выражение a < a+1
также выполняет это требование.
Да, вы упускаете тот факт, что, хотя y
сам инициализируется с постоянным выражением, это не такой же как &y
,
Адрес y
может сильно варьироваться в зависимости от вашей истории стека вызовов.
Пункт 3 C++11 5.19 Constant expressions
устанавливает условия, при которых оператор адреса можно считать константным выражением (как основные выражения констант, описанные в параграфе 2, могут превращаться в «реальные» константные выражения):
… Выражение константы адреса является основным выражением константы типа указателя, которое оценивается как адрес объекта со статической продолжительностью хранения, в адрес функции, или к значение нулевого указателя, или prvalue выражение ядра константы типа std :: nullptr_t. В совокупности буквенные константные выражения, ссылочные константные выражения и адресные константные выражения называются константными выражениями.
поскольку &y
это ничего из того, что не считается постоянным выражением.
Взятие адреса чего-либо здесь не является причиной, а скорее сравнение указателя с использованием operator<
на не связанные объекты.
Реляционные операторы для указателей указываются только для указателей на объекты в том же классе или в массиве (5.9. Реляционные операторы [expr.rel], пункты 3 и 4.) Реляционное сравнение для указателей на несвязанные объекты не определено.
Сравнивая адрес для равенство скорее, чем заказ работает:
int x = 42;
void f()
{
int y = 43;
static_assert(&x != &y, "foo");
^^ <--- "<" on unrelated objects is unspecified
}
int main()
{
f();
}
Просто чтобы показать, что это не имеет ничего общего с const-выражениями как таковыми,
void f()
{
int y[2] = { 42, 43 };
static_assert(&y[0] < &y[1], "foo");
^ <--- "<" on objects within an array is specified
}
int main()
{
f();
}
Другая живой пример.
5.19p2 не определяет постоянные выражения, это определяет основные константные выражения.
выражение основной константы становится только постоянное выражение если это соответствует одному из правил в 5.19p3. Там соответствующая часть уже была указана Джроком:
адресное выражение является основным выражением типа prvalue типа указателя, которое вычисляется по адресу объекта со статической продолжительностью хранения, по адресу функции или по нулевому значению указателя, или по выражению ядра постоянной константы типа
std::nullptr_t
,
Ваше основное постоянное выражение &y
не оценивает ни один из тех, так что это не адресное постоянное выражение, и, следовательно, не постоянное выражение.
Извините, я согласен, что предыдущий ответ, вероятно, был неправильным чтением пунктов. Вместо этого актуальным соответствующим условием является пункт 5.19 [expr.const], который гласит (выделение добавлено):
Выражение литеральной константы является основным выражением константы литерального типа, но не тип указателя.
Выражение целочисленной константы — это выражение литеральной константы целочисленного или незаданного типа перечисления. [ Заметка:
Такие выражения могут использоваться как границы массивов (8.3.4, 5.3.4), как длины битовых полей (9.6), как инициализаторы перечислителя
если базовый тип не является фиксированным (7.2), как константы нулевого указателя (4.10) и как выравнивания (7.6.2). -конец
примечание] Преобразованное константное выражение типа T — это буквальное константное выражение, неявно преобразуемое в тип T,
где неявное преобразование (если оно есть) разрешено в выражении литеральной константы, а неявное преобразование
последовательность содержит только определенные пользователем преобразования, преобразования lvalue в rvalue (4.1), интегральные преобразования (4.5),
и интегральные преобразования (4.7), кроме сужающих преобразований (8.5.4). [Примечание: такие выражения могут быть использованы
как выражения случая (6.4.2), как инициализаторы перечислителя, если базовый тип является фиксированным (7.2), и как интеграл или
перечисление нетиповых аргументов шаблона (14.3). — конец примечания] Ссылочное константное выражение является lvalue
выражение основной константы, которое обозначает объект со статической продолжительностью хранения или функцию. Адрес
постоянное выражение является основным значением константы выражения типа указателя, которое оценивается как адрес
объект со статической продолжительностью хранения, по адресу функции, или нулевому значению указателя, или первичному ядру
константное выражение типа std :: nullptr_t. В совокупности буквенные константные выражения, ссылочная константа
выражения и адреса постоянные выражения называются постоянными выражениями.
Основное константное выражение напрямую не является константным выражением, есть дополнительные условия, которые изложены в этом третьем абзаце.
В C ++ до C ++ 11:
Другие выражения [кроме целочисленных константных выражений] считаются постоянными выражениями только с целью
[…]
инициализация нелокального статического объекта (3.6.2). такие
константные выражения должны принимать одно из следующих значений:— выражение константы адреса,
[…]Выражение константы адреса является указателем на lvalue
обозначение объекта статической длительности хранения, строка
литерал (2.13.4) или функция.
поскольку y
не имеет статической длительности хранения, &y
не будет
быть постоянным выражением.
C ++ 11, кажется, изменил это; Я подозреваю, что это
перерасход, однако. (C ++ до C ++ 11 перечисляет вещи, которые
константные выражения. C ++ 11 перечисляет то, чего нет. Это
было бы легко забыть.)
Независимо от того, конечно: ваше сравнение не может быть использовано в стандарте
C ++; результаты сравнения двух адресов, которые не указывают
в тот же объект не уточняется. (С другой стороны, у меня есть
иногда используется нечто подобное в машинно-зависимом коде.
Не статично, но на платформах, таких как Linux на ПК или Solaris,
можно определить, указывает ли указатель на объект
со статическим временем жизни и автоматической переменной или динамически
выделил память такими трюками.)
Ответчик paxdiablo процитировал отрывок, в котором я не нашел
мое чтение C ++ 11; C ++ 11 следует тому же правилу, что и C ++ до 11
в этом отношении и для того, чтобы быть постоянным адресом
выражение, адрес должен быть адрес объекта со статическим
время жизни (или функция, или нулевой указатель).