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

Если структурированные привязки не могут быть constexpr, почему они могут использоваться в функции constexpr?

В соответствии с этим ответом, по-видимому, нет веской причины, по которой структурированные привязки не могут быть constexpr, но стандарт по-прежнему запрещает его. В этом случае, однако, не следует запрещать использование структурированных привязок внутри функции constexpr? Рассмотрим простой фрагмент:

#include <utility>

constexpr int foo(std::pair<int, int> p) {
    auto [a, b] = p;
    return a;
}

int main() {
    constexpr int a = foo({1, 2});
    static_assert(a == 1);
}

Оба gcc и clang не вызывают сбоев в сборе код. Является ли код плохо сформированным в любом случае или это действительно разрешено?

4b9b3361

Ответ 1

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

Короче: функция constexpr может означать постоянное выражение, но инициализация константного выражения не нуждается в том, что связанное объявление имеет спецификатор constexpr.

Вы можете проверить это в стандарте С++ [dcl.constexpr]:

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

- вызов функции constexpr может появляться в константном выражении [...]

Это оценка выражения, которое определяет, является ли выражение константное выражение [expr.const]:

Выражение e является выражением основной константы, если оценка для e [...] не оценивает одно из следующих выражений [...]

Объявление не является выражением, поэтому инициализация объявляемого объекта является константным выражением независимо от наличия или отсутствия спецификатора constexpr в объявлении.

Наконец, в [dcl.constexpr] указано, что функция constexpr должна быть такой, чтобы существовали параметры, для которых его тело можно оценить как константное выражение:

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

Когда вы объявляете constexpr int a, компилятор ожидает, чтобы a был инициализирован константным выражением, а выражение foo({1,2}) является постоянным выражением, поэтому ваш код хорошо сформирован.

PS: Тем не менее, спецификаторы объявлений (static, thread_local = > static) в объявлении локальной функции функции подразумевают, что функция не может быть объявлена ​​ constexpr.

Ответ 2

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

существует по крайней мере один набор значений аргумента, так что вызов функции может быть оцененным подвыражением (для конструкторов, использование в константе инициализатор достаточен) (поскольку С++ 14). Диагностика не требуется за нарушение этой пули.

Обратите внимание на последнее предложение. Компилятор может, но не обязан, бросать красный флаг.

Ключевым требованием является только то, что функция имеет некоторый набор значений параметров, что приводит к постоянному результату от функции (и тело функции соответствует перечисленным требованиям). Например, функция может использовать структурированное привязку условно; но для некоторого набора значений параметров делайте что-то еще, создавая постоянный результат. Это будет отмечать этот флажок для функции constexpr.

Но, несмотря на сложность современных компиляторов С++, они могут не обязательно быть способны достичь этого определения в каждом возможном случае, поэтому на практике было бы трудно обеспечить выполнение такого требования, поэтому компиляторам разрешено просто считать это как должное.