Вложенные вызовы функции constexpr перед определением в контексте константного выражения

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

constexpr int f();

constexpr int g() {
    return f();
}

constexpr int f() {
    return 42;
}

int main() {
    constexpr int i = g();
    return i;
}

Это компилируется без проблем и работает. Перемещение определения f за пределы основных триггеров error: 'constexpr int f()' used before its definition, как и следовало ожидать.

Я предполагаю, что это работает, потому что f был определен до вызова g, поэтому оба вызова являются константными выражениями.

Почему f() и g() явно являются выражениями-константами, хотя f не определено, когда оно вызывается g? Как это описано в Стандарте?

Я проверил это на GCC 6.1.0 и Clang 3.8.0 от Coliru.


person Quentin    schedule 30.05.2016    source источник
comment
5.20/(2.3): разве что... вызов неопределенной функции constexpr?   -  person Kerrek SB    schedule 30.05.2016
comment
Я думаю, что мы должны подчеркнуть тот факт, что функция constexpr должна иметь определение только тогда, когда она становится odr-used. Таким образом, хотя f() не имеет определения в g(), компилятор может угадать тело g() с помощью простого объявления, но для фактического вызова g() вам нужно определение f(), потому что вызов функции считается odr-use. Так что да, данный ответ действителен, но я думаю, что именно это смутило ОП.   -  person KABoissonneault    schedule 30.05.2016
comment
См. CWG2166.   -  person T.C.    schedule 30.05.2016
comment
@Т.С. Это похоже на ответ :)   -  person Quentin    schedule 01.06.2016


Ответы (2)


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

§5.20/p2 Константные выражения [expr.const] (Выделено мое):

Условное-выражение e является основным константным выражением, если только вычисление e по правилам абстрактной машины (1.9) не будет оценивать одно из следующих выражений:

...

(2.3) — вызов неопределенной функции constexpr или неопределенного конструктора constexpr;

person 101010    schedule 30.05.2016
comment
Извините, я думаю, что мой вопрос не был ясен. Я спрашиваю, почему вызов является на самом деле константным выражением, даже если f не определено в момент его вызова? - person Quentin; 30.05.2016
comment
Ваша цитата может быть прочитана как разрешающая, так и запрещающая код OP, неясно, как вы ее читаете, и неясно, является ли это чтение (каким бы оно ни было) правильным. - person ; 30.05.2016
comment
Учитывая ваше редактирование: это оставляет важный вопрос без ответа. Делает ли [dcl.constexpr]p5 (для нешаблонной, не используемой по умолчанию функции constexpr [...], если не существует значений аргументов, таких, что вызов функции [...] может быть оцененным подвыражением ядра постоянное выражение (5.19), программа имеет неправильный формат, диагностика не требуется.) разрешить компилятору немедленно выдавать сообщение об ошибке после определения g? До определения f не было значений аргументов, чтобы сделать результат вычисления g() константным выражением. - person ; 30.05.2016
comment
@hvd Хммм ... Я считаю, что это спорно, поскольку диагностика не требуется. - person 101010; 30.05.2016
comment
Я думаю, что @hvd относится к тому, что я пытаюсь выразить - при компиляции g f() не может быть константным выражением, так почему же решение откладывается до фактического вызова g() ? - person Quentin; 30.05.2016
comment
@Quentin В этот момент программа плохо сформирована, но диагностика не требуется. То есть компилятор не обязан выдавать диагностику/ошибку. Таким образом, вы получите ошибку позже, когда попытаетесь инициализировать constexpr неконстантным выражением. - person 101010; 30.05.2016

По ссылке T.C. в его комментарий на это распространяется отчет о дефекте.

Согласно 5.20 [expr.const], пункт 2.3, выражение является константным выражением, если (среди прочих причин) оно не будет вычислять

  • вызов неопределенной constexpr функции или неопределенного constexpr конструктора;

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

Это дает понять, что пример построен правильно и действительно должен работать так, как ожидалось, если f определено до вызова g.

person Quentin    schedule 02.06.2016