Связь между constexpr и чистыми функциями

Я прав, что:

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

И если да, то почему arent <cmath> функции, определенные с помощью constexpr?

Ответ 1

Чтобы добавить к сказанному, рассмотрим следующий шаблон функции constexpr:

template <typename T>
constexpr T add(T x, T y) { return x + y; }

Этот шаблон constexpr можно использовать в константном выражении в некоторых случаях (например, где T есть int), но не в других (например, где T - тип класса с operator+ перегрузка, которая не объявлена ​​constexpr).

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

(Существуют аналогичные примеры, включающие функции без шаблонов.)

Ответ 2

В дополнение к предыдущим ответам: constexpr на функции ограничивает его реализация значительно: его тело должно быть видимым компилятору (inline) и должен состоять только из одного оператора return. я бы удивлен, если вы можете правильно реализовать sqrt() или sin() выполните это последнее условие.

Ответ 3

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

Последний, используя код шаблона, позволяет нам продемонстрировать нечистую функцию constexpr:

template <typename T>
constexpr T add(T lhs, T rhs) { return lhs + rhs; }

созданный с помощью этого типа

DebugInteger operator+(DebugInteger lhs, DebugInteger rhs) {
  printf("operator+ %i %i", lhs._value, rhs._value);
  return DebugInteger(lhs._value + rhs._value);
}

Здесь operator+ не является constexpr и, следовательно, может читать/записывать глобальное состояние.

Можно сказать, что функция constexpr pure оценивается во время компиляции... но затем она просто заменяется константой в отношении времени выполнения.

Ответ 4

Каждая функция constexpr чиста, но не каждая чистая функция может или должна быть constexpr.

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

Чистая функция - это функция, которая зависит только от ее аргументов или от другого постоянного состояния. Это в значительной степени функция constexpr. Кроме того, функции constexpr должны быть определены (не только объявлены) до их первого использования (рекурсия, кажется, разрешена, хотя) и должна состоять только из оператора return. Этого достаточно, чтобы сделать разрешенное подмножество Turing-complete, но результат не обязательно является наиболее эффективной формой во время выполнения.

Это приводит нас к математическим функциям. Вероятно, вы можете реализовать constexpr sqrt() или sin(), но им придется использовать рекурсивную реализацию, которую компилятор может оценить во время компиляции, тогда как во время выполнения они будут лучше реализованы в одной операции ассемблера. Поскольку constexpr использование sqrt() и sin() немного и далеко друг от друга, лучше использовать максимальную производительность во время выполнения, для чего требуется форма, которая не способна constexpr.

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