Мне кажется, что наличие «функции, которая всегда возвращает 5» нарушает или ослабляет значение «вызова функции». Должна быть причина, или необходимость в этой возможности, иначе ее не будет в C ++ 11. Почему это там?
// preprocessor.
#define MEANING_OF_LIFE 42
// constants:
const int MeaningOfLife = 42;
// constexpr-function:
constexpr int MeaningOfLife () { return 42; }
Мне кажется, что если бы я написал функцию, возвращающую буквальное значение, и подошел к проверке кода, кто-то сказал бы мне, я должен был бы объявить постоянное значение вместо написания return 5.
constexpr
? Если так, я могу видеть использование.const
. В самом деле, уполномочено намерение это полезно ! Размеры массива являются каноническим примером.Ответы:
Предположим, что-то немного сложнее.
Теперь у вас есть что-то, что можно оценить до постоянной, сохраняя при этом хорошую читабельность и позволяя немного более сложную обработку, чем просто установка постоянной для числа.
Это в основном обеспечивает хорошую поддержку для ремонтопригодности, поскольку становится более очевидным, что вы делаете. Взять
max( a, b )
к примеру:Это довольно простой выбор, но это означает, что если вы вызываете
max
с постоянными значениями, он явно вычисляется во время компиляции, а не во время выполнения.Другим хорошим примером будет
DegreesToRadians
функция. Каждый находит градусы легче, чем радианы. Хотя вы, возможно, знаете, что в радианах указано 180 градусов, это гораздо яснее записать следующим образом:Много полезной информации здесь:
http://en.cppreference.com/w/cpp/language/constexpr
источник
Введение
constexpr
не был представлен как способ сказать реализации, что что-то может быть оценено в контексте, который требует константного выражения ; Соответствующие реализации смогли доказать это до C ++ 11.То, что реализация не может доказать, является намерением определенного фрагмента кода:
Чего бы не было в мире
constexpr
?Допустим, вы разрабатываете библиотеку и понимаете, что хотите вычислить сумму каждого целого числа в интервале
(0,N]
.Отсутствие намерения
Компилятор может легко доказать, что вышеуказанная функция вызывается в константном выражении, если переданный аргумент известен во время перевода; но вы не объявили это намерением - так уж случилось.
Теперь приходит кто-то другой, читает вашу функцию, выполняет тот же анализ, что и компилятор; « О, эту функцию можно использовать в постоянном выражении!» и пишет следующий фрагмент кода.
Оптимизация
Вы, как «замечательный» разработчик библиотек, решаете, что
f
следует кешировать результат при вызове; кто хотел бы рассчитывать один и тот же набор значений снова и снова?Результат
Внедрив свою глупую оптимизацию, вы просто прервали каждое использование своей функции в контексте, где требовалось постоянное выражение .
Вы никогда не обещали, что функция будет использоваться в постоянном выражении , и без этого
constexpr
не было бы способа обеспечить такое обещание.Итак, зачем нам это
constexpr
?Основное использование constexpr - объявить намерение .
Если объект не помечен как
constexpr
- он никогда не предназначался для использования в константном выражении ; и даже если это так, мы полагаемся на компилятор для диагностики такого контекста (потому что он игнорирует наши намерения).источник
constexpr
выражениях. Другими словами, почти все можно аннотироватьconstexpr
(может быть, однажды это просто исчезнет из-за этого?), И если у кого-то нет критерия, когда его использовать,constexpr
или нет, почти весь код будет написан как таковой. ,I/O
,syscall
иdynamic memory allocation
определенно не можите быть помечено какconstexpr
Кроме того, не все должно бытьconstexpr
.constexpr
является гарантией некоторого поведения. Так же, как иconst
делает.int f (int n) { return n > 0 ? n + f (n-1) : n;} T arr[f(10)];
Я не могу заставить это компилировать где-нибудь?Возьмите
std::numeric_limits<T>::max()
: по какой-то причине, это метод.constexpr
было бы полезно здесь.Другой пример: вы хотите объявить C-массив (или a
std::array
) размером с другой массив. Способ сделать это на данный момент так:Но не лучше ли было бы написать:
Благодаря
constexpr
, вы можете:источник
constexpr
заставляет компилятор заставить функцию возвращать значение времени компиляции (если это возможно).constexpr
него его нельзя использовать ни в объявлении размера массива, ни в качестве аргумента шаблона, независимо от того, является ли результат вызова функции константой времени компиляции или нет. Эти два в основном являются единственными вариантами использования для,constexpr
но по крайней мере вариант использования аргумента шаблона является своего рода важным.-pedantic
опцию, и она будет помечена как ошибка.constexpr
функции действительно хороши и являются отличным дополнением к c ++. Тем не менее, вы правы в том, что большинство проблем, которые он решает, можно обойтись без макросов.Однако одно из применений
constexpr
не имеет C ++ 03 эквивалентных типизированных констант.источник
four
не разрешается. Я должен был действительно копать, чтобы понять, кто берёт адрес моейstatic const
переменной.four
ни другоеfive
.enum class
тип, он исправляет некоторые проблемы перечисления.Из того, что я прочитал, необходимость в constexpr проистекает из проблемы метапрограммирования. Классы черт могут иметь константы, представленные в виде функций, подумайте: numeric_limits :: max (). С constexpr эти типы функций могут использоваться в метапрограммировании или в качестве границ массивов и т. Д. И т. Д.
Еще один пример, который я бы выбрал, - это то, что для интерфейсов классов вы можете захотеть, чтобы производные типы определяли свои собственные константы для некоторой операции.
Редактировать:
После изучения SO, похоже, что другие придумали несколько примеров того, что может быть возможно с constexprs.
источник
constexpr
более конкретно, полезен в компиляторе с мощной системой оценки выражений во время компиляции. C ++ действительно не имеет аналогов в этом домене. (это сильная похвала для C ++ 11, ИМХО)Из выступления Страуструпа на «Going Native 2012»:
источник
Другое использование (еще не упомянутое) -
constexpr
конструкторы. Это позволяет создавать константы времени компиляции, которые не нужно инициализировать во время выполнения.Соедините это с пользовательскими литералами, и вы получите полную поддержку литеральных пользовательских классов.
источник
Раньше был шаблон с метапрограммированием:
Я полагаю, что это
constexpr
было введено, чтобы позволить вам писать такие конструкции без необходимости в шаблонах и странных конструкциях со специализацией, SFINAE и прочим, но точно так же, как вы бы написали функцию времени выполнения, но с гарантией того, что результат будет определен при компиляции. -время.Однако обратите внимание, что:
Скомпилируйте это с,
g++ -O3
и вы увидите, чтоfact(10)
это действительно оценивается во время компиляции!Компилятор с поддержкой VLA (например, компилятор C в режиме C99 или компилятор C ++ с расширениями C99) может даже позволить вам сделать:
Но то, что это нестандартный C ++ на данный момент -
constexpr
похоже на способ борьбы с этим (даже без VLA, в приведенном выше случае). И все еще существует проблема необходимости иметь «формальные» константные выражения в качестве аргументов шаблона.источник
std::array<int, fact(2)>
и вы увидите, что fact () не оценивается во время компиляции. Это просто оптимизатор GCC делает хорошую работу.Только что начали переключать проект на c ++ 11 и столкнулись с совершенно хорошей ситуацией для constexpr, которая убирает альтернативные методы выполнения той же операции. Ключевым моментом здесь является то, что вы можете поместить функцию в объявление размера массива только тогда, когда она объявлена constexpr. Есть ряд ситуаций, когда я вижу, что это очень полезно для продвижения вперед в той области кода, в которой я участвую.
источник
static inline constexpr const auto
наверное лучше.Все остальные ответы великолепны, я просто хочу привести классный пример того, что вы можете сделать с constexpr, что удивительно. See-Phit ( https://github.com/rep-movsd/see-phit/blob/master/seephit.h ) - это HTML-анализатор и шаблонизатор времени компиляции. Это означает, что вы можете вставить HTML и получить дерево, которым можно манипулировать. Выполнение анализа во время компиляции может дать вам дополнительную производительность.
Из примера страницы github:
источник
Ваш основной пример служит тем же аргументом, что и аргумент самих констант. Зачем использовать
над
Потому что это намного удобнее в обслуживании. Использование constexpr намного, намного быстрее, чтобы писать и читать, чем существующие методы метапрограммирования.
источник
Это может включить некоторые новые оптимизации.
const
Традиционно является подсказкой для системы типов и не может использоваться для оптимизации (например,const
функция-член можетconst_cast
и в любом случае изменять объект юридически, поэтомуconst
нельзя доверять оптимизации).constexpr
означает, что выражение действительно является константой, если входные данные для функции являются постоянными. Рассматривать:Если это раскрыто в каком-то другом модуле, компилятор не может доверять тому, что
GetNumber()
он не будет возвращать разные значения при каждом вызове - даже последовательно без промежуточных вызовов между ними - потому чтоconst
мог быть отброшен в реализации. (Очевидно, что любой программист, который сделал это, должен быть застрелен, но язык это позволяет, поэтому компилятор должен соблюдать правила.)Добавление
constexpr
:Теперь компилятор может применить оптимизацию, когда возвращаемое значение
GetNumber()
кэшируется, и исключать дополнительные вызовыGetNumber()
, посколькуconstexpr
это более надежная гарантия того, что возвращаемое значение не изменится.источник
const
может использоваться в оптимизации ... Это неопределенное поведение, чтобы изменить значение, определенное const, даже послеconst_cast
IIRC. Я ожидаю, что это будет согласовано сconst
функциями-членами, но мне нужно проверить это со стандартом. Это будет означать, что компилятор может безопасно выполнять оптимизацию там.int x
противconst int x
), его можно безопасно изменить,const_cast
убрав const из указателя / ссылки на него. В противном случаеconst_cast
он всегда вызывал бы неопределенное поведение и был бы бесполезен :). В этом случае компилятор не имеет информации о постоянстве исходного объекта, поэтому он не может этого сказать.int GetNumber() const = 0;
) должен объявитьGetNumber()
метод виртуальным. Метод second (constexpr int GetNumber() const = 0;
) недопустим, поскольку чистый спецификатор (= 0
) подразумевает, что метод является виртуальным, но constexpr не должен быть виртуальным (ref: en.cppreference.com/w/cpp/language/constexpr )Когда использовать
constexpr
:источник
constexpr
следует отдавать предпочтение макросам препроцессора илиconst
.Это полезно для чего-то вроде
Свяжите это с классом черт или подобным, и это станет весьма полезным.
источник