Является gcc, считая встроенные функции не константных выражений постоянными выражениями

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

Этот вопрос Почему я могу вызвать функцию 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);
}

Который, как я отвечаю, плохо сформирован, но gcc 4.8.2 позволяет ему (видеть его в прямом эфире).

Но если мы используем флаг -fno-builtin gcc, генерируем ошибку (см. его в прямом эфире):

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

поэтому seems, что gcc рассматривает встроенную версию printf как константное выражение. gcc здесь встроены документы, но не документирует этот случай, когда встроенное значение функции, отличной от constexpr, может считаться константным выражением.

Если это действительно так:

  • Разрешено ли компилятору сделать это?
  • Если они разрешены, им не нужно документировать, чтобы они были совместимыми?
  • Можно ли это считать расширением, если это так, похоже, это потребует предупреждения как С++ draft standard раздел 1.4 В соответствии с пунктом 8 выполнения, в котором говорится (подчеркивает мой):

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

Обновить

Как указывает Кейси, в исходной проблеме происходит несколько вещей, которые делают ее плохим примером. Простым примером будет использование std:: pow, который не является функцией constexpr:

#include <cmath>
#include <cstdio>

constexpr double f()
{
    return std::pow( 2.0, 2.0 ) ;
}

int main()
{
    constexpr double x = f() ;

    printf( "%f\n", x ) ;
}

Скомпилирует и создает без предупреждений или ошибок (видеть его в прямом эфире), но добавление -fno-builtin заставляет его генерировать ошибку (видеть его в прямом эфире). Примечание: почему математические функции не являются constexpr в С++ 11:

error: call to non-constexpr function 'double pow(double, double)'
     return std::pow( 2.0, 2.0 ) ;
                               ^

Ответ 1

Да, gcc рассматривает некоторые встроенные функции как constexpr, даже если стандарт явно не маркирует их как таковые. Мы можем найти обсуждение, которое относится конкретно к математической функции, найденной в cmath в отчете об ошибке gcc [С++ 0x] sinh vs asinh vs constexpr, в котором говорится:

LWG 2013, похоже, позволяет GCC рассматривать эти функции как constexpr. Итак, исправлено для 4.7

который ссылается на LWG issue 2013, оригинальная предлагаемая резолюция должна заключаться в следующем: 17.6.5.6 [constexpr.functions] ( акцент мой вперед):

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

но после С++ 11 разрешение было отменено, а окончательное разрешение закончилось следующим образом:

[...] Реализация не должна объявлять любую стандартную библиотечную функцию подпись как constexpr, за исключением тех, где она явно требуется. [..]

Итак, это в настоящее время (в С++ 14) явно несоответствующее расширение, и насколько я могу судить, это было несоответствие в С++ 11, поскольку он изменяет наблюдаемое поведение и, следовательно, не будет разрешен с помощью правила as-if.

Джонатан Вакели указывает на обсуждение списка рассылки libstdc++: PR libstdС++/49813 revisited: constexpr для функций (и встроенных функций), где повторное открытие упомянутый выше отчет об ошибке был обсужден в связи с вышеизложенными вопросами:

Я считаю, что мы должны снова открыть ошибку в свете фактического разрешения LWG 2013 (добавление constexpr запрещено).

FE не следует рассматривать встроенные функции как constexpr в строгом соответствии Режим.

Мы должны либо полностью удалить _GLIBCXX_CONSTEXPR из <cmath> или сделать его условным на __STRICT_ANSI __.

Ответ 2

GCC не считает, что f() является постоянным выражением. Посмотрите на диагностику первой примерной программы, которую вы связали:

main.cpp: In function 'int main()':
main.cpp:10:19: warning: ISO C++ forbids variable length array 'a' [-Wvla]
         char a[f()];
                   ^

Компилятор не считает, что f() является константным выражением, программа фактически использует расширение GCC, которое позволяет массивы переменной длины - массивы с непостоянным размером.

Если вы измените программу, чтобы заставить f() в постоянное выражение:

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

GCC сообщает об ошибке:

main.cpp: In function 'int main()':
main.cpp:10:32:   in constexpr expansion of 'f()'
main.cpp:5:41: error: 'printf(((const char*)"a side effect!\012"))' is not a constant expression
         return printf("a side effect!\n");
                                         ^