GCC рассматривает встроенные функции непостоянных выражений как постоянные выражения

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

Этот вопрос Почему я могу вызвать функцию не-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 встроенные документы здесь но не документирует этот случай, когда встроенную функцию non-constexpr можно считать константным выражением.

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

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

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

Обновить

Как указывает Кейси, в исходной задаче происходит несколько вещей, которые делают ее плохим примером. Простой пример будет использовать станд :: пау которая не является функцией 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 в C ++ 11:

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

11

Решение

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

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

что относится к Выпуск LWG 2013 чья первоначальная предложенная резолюция заключалась в добавлении следующего 17.6.5.6 [constexpr.functions] (Акцент шахты идет вперед):

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

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

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

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

Джонатан Уэйкли указывает на libstdc++ обсуждение списка рассылки: PR libstdc ++ / 49813 вновь: constexpr о функциях (и встроенных) где повторное открытие сообщения об ошибке, упомянутого выше, обсуждалось из-за проблем, изложенных выше:

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

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

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

6

Другие решения

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

main.cpp: в функции 'int main ()':
main.cpp: 10: 19: предупреждение: ISO C ++ запрещает массив переменной длины '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: в функции 'int main ()':
main.cpp: 10: 32: в расширении constexpr 'f ()'
main.cpp: 5: 41: ошибка: 'printf (((const char *) "побочный эффект! \ 012")) "не является константным выражением
return printf ("побочный эффект! \ n");
^
4