Почему я могу вызвать функцию non-constexpr внутри функции constexpr?

Рассмотрим следующий код:

#include <stdio.h>

constexpr int f()
{
    return printf("a side effect!\n");
}

int main()
{
    char a[f()];
    printf("%zd\n", sizeof a);
}

Я ожидал, что компилятор будет жаловаться на вызов printf внутри f, потому что f должен быть constexpr, но printf - нет. Почему программа компилирует и печатает 15?

Ответ 1

Программа плохо сформирована и не требует диагностики в соответствии с С++ 11 черновик проекта 7.1.5 Параметр спецификатора constexpr 5, в котором говорится:

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

и предоставляет следующий пример:

constexpr int f(bool b)
  { return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required

и раздел 5.19 в абзаце 2 говорится:

Условное выражение является выражением постоянной константы, если оно включает одно из следующего в качестве потенциально оцениваемого подвыражения [...]

и включает в себя:

- вызов функции, отличной от конструктора constexpr для литеральный класс или функция constexpr [Примечание: разрешение перегрузки (13.3) применяется как обычно - примечание];

Мы бы предпочли диагностику в этом случае, это может быть просто недосмотр, у меня есть отчет об ошибке для аналогичной ситуации, когда gcc не вызывает ошибку, но нам бы это понравилось: Является ли компилятор разрешенным лишением в том, что он считает undefined поведение в постоянном выражении?.

Обновить

Использование флага -fno-builtin приведет к тому, что gcc создаст следующую ошибку:

 error: call to non-constexpr function 'int printf(const char*, ...)'
 return printf("a side effect!\n");
                                 ^

Итак, gcc считает, что это плохо сформированное, оно просто игнорирует его, когда использует встроенную версию printf.

Хотя несколько непоследовательно использование -pedantic вызывает следующее предупреждение:

warning: ISO C++ forbids variable length array 'a' [-Wvla]
 char a[f()];
           ^

Обратите внимание, что с помощью f() для инициализации переменной constexpr:

constexpr int x = f() ;

создает ошибку:

error: 'printf(((const char*)"a side effect!\012"))' is not a constant expression

Обратите внимание, что дополнительно в более общем случае компилятору не разрешено отмечать стандартные библиотечные функции как constexpr если явно не разрешено стандартом.