Если структурированные привязки не могут быть 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 не вызывают проблем при компиляции кода. Является ли код неверным в любом случае, или этот действительно разрешен?

2 ответа

Решение

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

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

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

Вызов функции constexpr дает тот же результат, что и вызов эквивалентной функции non-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,

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

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

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

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

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

Другие вопросы по тегам