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

Принимает ли адрес локальной переменной постоянное выражение в С++ 11?

Следующая программа С++ 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-to-rvalue...

но если я ошибаюсь (?), в программе нет преобразований lvalue-rvalue.

и

a id-expression, который ссылается на переменную [snip], если только:

  • он инициализируется константным выражением

который y есть - он инициализируется константным выражением 43.

Итак, это ошибка в стандарте, или я чего-то не хватает?

Update:

Это сбивает с толку, но я думаю, что я на высоте, поэтому позвольте мне показать пример, который будет показывать, что происходит:

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.

4b9b3361

Ответ 1

Попробуйте определить, какие требования должно выполнять выражение в объявлении static_assert, шаг за шагом, используя n3485.

[dcl.dcl]/1

static_assert-declaration:
    static_assert ( константа-выражение , строка-буквальный ) ;

[dcl.dcl]/4

В объявлении static_assert константное выражение должно быть константным выражением, которое может быть контекстно преобразовано в bool.

[expr.const]/4

В совокупности литеральные константные выражения, ссылочные константные выражения и постоянные выражения адресов называются константными выражениями.


Какой тип постоянного выражения &x < &y? Это не выражение константы адреса:

[expr.const]/4

Выражение константы адреса является выражением константы основного значения prvalue (после преобразований, требуемых контекстом) типа std::nullptr_t или типа указателя [...].

Тип &x < &y равен bool в соответствии с [expr.rel]/1.

Это тоже не ссылочное константное выражение, поэтому оно должно быть литеральным постоянным выражением , если оно есть.

Литеральное постоянное выражение представляет собой константное выражение константы prvalue типа literal [...]

Следовательно, &x < &y должен удовлетворять требованиям основного постоянного выражения.


Как отмеченные TemplateRex и hvd в комментариях, в данном конкретном случае &x < &y не удовлетворяет требованиям основного постоянного выражения:

[expr.const]/2

[выражение константы ядра не должно содержать] реляционный или оператор равенства, где результат не указан;

[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 также выполняет это требование.

Ответ 2

Да, вам не хватает того, что, хотя y сам инициализируется константным выражением, это не то же самое, что &y.

Адрес y может сильно отличаться в зависимости от истории стека вызовов.

В абзаце 3 раздела C++11 5.19 Constant expressions указаны условия, при которых адрес-оператор можно считать постоянным выражением (как выражать основные выражения констант, указанные в параграфе 2, в "реальные" постоянные выражения):

... Выражение константы адреса - это выражение константы основного значения prvalue типа указателя, которое оценивает адрес объекта со статической продолжительностью хранения, до адрес функции, или значение нулевого указателя, или выражение константы основного значения prtyue типа std:: nullptr_t. В совокупности используются литералы с постоянными выражениями, ссылочные константные выражения и постоянная адреса выражения называются константными выражениями.

Так как &y не является ни одной из этих вещей, он не считается постоянным выражением.

Ответ 3

Взятие адреса чего-то здесь не является виновником, а скорее сравнение указателей с использованием 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();
}

Другой живой пример.

Ответ 4

Извините, я согласен с тем, что предыдущий ответ был, вероятно, неправильным чтением предметов. Вместо этого фактическое релевантное предложение равно 5.19 [expr.const], пункт 3, который гласит (добавление выделения):

Литеральное постоянное выражение представляет собой выражение константы основного значения типа prvalue типа literal, , но не тип указателя. интегральное постоянное выражение - это литеральное постоянное выражение интегрального или неперечисленного типа перечисления. [ Заметка: Такие выражения могут использоваться как границы массива (8.3.4, 5.3.4) в качестве длины битового поля (9.6), так как инициализаторы перечислителя если базовый тип не является фиксированным (7.2), как константы нулевого указателя (4.10) и как выравнивания (7.6.2). -конец note] Преобразованное константное выражение типа T является литеральным постоянным выражением, неявно преобразованным в тип T, где неявное преобразование (если оно есть) разрешено в литеральном постоянном выражении и неявное преобразование последовательность содержит только пользовательские преобразования, преобразования lvalue-to-rvalue (4.1), интегральные рекламные акции (4.5), и интегральные преобразования (4.7), кроме сужения конверсий (8.5.4). [Примечание: такие выражения могут использоваться как выражений случая (6.4.2), в качестве инициализаторов перечислителя, если базовый тип фиксирован (7.2), и как интегральный или перечисление аргументов шаблона non-type (14.3). -end note] Выражение константы ссылки является значением lvalue которое обозначает объект со статической продолжительностью хранения или функцией. Адрес константное выражение - это константное выражение константы prvalue типа указателя, которое оценивает адрес объект со статической продолжительностью хранения, к адресу функции или нулевому указателю или ядру prvalue постоянное выражение типа std:: nullptr_t. В совокупности, литеральные константные выражения, эталонная константа выражения и выражения констант адреса называются константными выражениями.

Ядро константное выражение не является прямым постоянным выражением, есть дополнительные условия, которые изложены в этом третьем абзаце.

Ответ 5

5.19p2 не определяет константные выражения, он определяет основные константные выражения.

Основное константное выражение становится постоянным выражением, если оно соответствует одному из правил в 5.19p3. Там соответствующая часть уже была указана jrok:

Выражение константы адреса представляет собой выражение константы основного значения prvalue типа указателя, которое оценивает адрес объекта со статической продолжительностью хранения, адресом функции или значением нулевого указателя или постоянным выражением основного значения prvalue введите std::nullptr_t.

Ваше основное константное выражение &y не оценивает ни одно из них, поэтому оно не является постоянным выражением адреса и, следовательно, не является постоянным выражением.

Ответ 6

В С++ pre-С++ 11:

Другие выражения [, чем интегральные постоянные выражения] считаются постоянными экспрессиями только с целью инициализация нелокального статического объекта (3.6.2). такие константные выражения должны оцениваться одним из следующих:

[...]

- выражение константы адреса,

[...]

Выражение константы адреса является указателем на lvalue обозначение объекта статической продолжительности хранения, строка литерал (2.13.4) или функцию.

Так как y не имеет статической продолжительности хранения, &y не будет быть постоянным выражением.

С++ 11, похоже, изменил это; Я подозреваю, что это oversignt, однако. (С++ pre-С++ 11 перечисляет вещи, которые постоянные выражения. В С++ 11 перечислены вещи, которые не являются. Это было бы легко забыть об этом.)

Независимо, конечно: ваше сравнение не используется в стандартном С++; результаты сравнения двух адресов, которые не указывают в тот же объект не указан. (С другой стороны, у меня есть иногда использовал что-то подобное в машинных зависимых кодах. Не статически, но на таких платформах, как Linux на ПК или Solaris, можно определить, указывает ли указатель на объект со статическим временем жизни и автоматической переменной или динамически выделенная память с такими трюками.)

EDIT:

Ансперс от paxdiablo процитировал прохождение, которое я не нашел в мое чтение С++ 11; С++ 11 следует тому же правилу, что и С++ pre-11 в этом отношении, и для того, чтобы быть постоянным адресом выражение, адрес должен быть адресом объекта со статическим время жизни (или функция или нулевой указатель).