Разве когда-нибудь плохо отмечать C ++ функцию constexpr?

26

Учитывая очень тривиальную функцию,

int transform(int val) {
    return (val + 7) / 8;
}

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

constexpr int transform(int val) {
    return (val + 7) / 8;
}

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

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

Xirema
источник
1
Единственное, о чем я мог думать, это ошибки компилятора. Возможно, что рекурсивный вызов функции constexpr может вызвать очень медленный шаг компиляции или даже сбой компилятора из памяти.
Zan Lynx

Ответы:

19

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

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

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

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

Если рассматриваемая функция не является частью стабильного общедоступного API, это не проблема, поскольку вы можете произвольно изменить дизайн по своему желанию. Но поскольку теперь вы контролируете все сайты вызовов, нет необходимости отмечать функцию constexpr «на всякий случай». Вы знаете , используете ли вы эту функцию в контексте constexpr. Добавление излишне ограничительных классификаторов может тогда рассматриваться как запутывание.

Амон
источник
12

Обозначение функции as constexprтакже делает ее встроенной функцией § [dcl.constexpr] / 1:

Функция или член статических данных, объявленные с помощью спецификатора constexpr, неявно являются встроенной функцией или переменной (7.1.6).

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

  1. ограничено для использования в одной единице перевода, или
  2. определено в заголовке.

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

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

constexprФункция также ограничена в некотором роде, так что для некоторых функций , которые она не может быть вариантом вообще. Ограничения включают в себя:

  1. виртуальных функций быть не может constexpr.
  2. его возвращаемый тип должен быть «литеральным типом» (например, нет объектов с нетривальными ctors или dtors).
  3. все его параметры должны быть литеральными типами.
  4. тело функции не может содержать tryблок.
  5. он не может содержать определение переменной не-литерального типа или что-либо со статической или продолжительностью хранения потока.

Я пропустил пару довольно непонятных вещей (например, они также не могут содержать gotoили asmвыражение), но вы поняли - для многих вещей это просто не сработает.

Итог: да, есть довольно много ситуаций, когда это было бы плохой идеей.

Джерри Гроб
источник
«оно не должно быть виртуальным (до C ++ 20)» Мне интересно, как виртуальная функция может быть constexpr? Что делают компиляторы?
хаосинк