Несколько дней назад я спросил, по какому критерию компилятор решает, вычислять ли функцию constexpr во время компиляции.
Когда функция constexpr оценивается во время компиляции?
Оказывается, constexpr вычисляется только во время компиляции, если все параметры являются константными выражениями, а переменная, которой вы присваиваете значение is, также является константным выражением.
template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo)
{
return (expo != 0 )? base * POW(base, expo -1) : 1;
}
template<typename T>
void foobar(T val)
{
std::cout << val << std::endl;
}
int main(int argc, char** argv)
{
foobar(POW((unsigned long long)2, 63));
return 0;
}
Если то, что мне сказали, является верным, этот пример кода является очень нецелесообразным, поскольку foobar не принимает constexpr (вы не можете использовать consexpr для параметров по какой-то причине), POW оценивается во время выполнения, даже если это было бы возможно вычислить его во время компиляции. Очевидное решение для принудительной оценки во время компиляции будет следующим:
auto expr = POW((unsigned long long)2, 63);
foobar(expr);
Это, однако, вынуждает меня использовать дополнительную строку кода, которая не требуется каждый раз, когда я хочу убедиться, что constexpr будет оценен во время компиляции. Чтобы сделать это немного более удобным, я придумал следующий сомнительный макрос:
#define FORCE_CT_EVAL(func) [](){constexpr auto ___expr = func; return std::move(___expr);}()
foobar(FORCE_CT_EVAL(POW((unsigned long long)2, 63)));
Несмотря на то, что он работает просто отлично, я чувствую, что что-то не так с этим. Влияет ли создание анонимной лямбды на производительность? Действительно ли возвращение по ссылке rvalue перемещает выражение в параметр функции? Как std :: move влияет на производительность? Есть ли лучшее решение для одного лайнера для этого?
Просто чтобы не оставлять это скрытым в комментариях
#include <type_traits>
#define COMPILATION_EVAL(e) (std::integral_constant<decltype(e), e>::value)
constexpr int f(int i){return i;}
int main()
{
int x = COMPILATION_EVAL(f(0));
}
Одно предостережение с этим подходом, constexpr
функции могут принимать числа с плавающей точкой и назначаться constexpr
Переменные с плавающей точкой, но вы не можете использовать тип с плавающей точкой в качестве нетипового параметра шаблона. Кроме того, те же ограничения для других видов литералов.
Ваша лямбда будет работать для этого, но я думаю, вам понадобится захват по умолчанию, чтобы получить значимое сообщение об ошибке, когда неconstexpr
вещи передаются в функцию. Это окончание std::move
Обязательно.
Э-э, твой лямбда-подход не работает для меня, я только что понял, как он может даже работать, лямбда не constexpr
функция. Это не должно работать на вас тоже.
Кажется, на самом деле нет другого пути, кроме как инициализировать constexpr
переменная в локальной области видимости.
О, хорошо, мой плохой, цель лямбды — просто оценка. Так что это работает для этого. Вместо этого его результатом является то, что он не может следовать за другим временем компиляции.
На C ++ 17, лямбды теперь можно использовать в constexpr
контексты, поэтому ограничение, указанное в EDIT2 / EDIT3, снято! Таким образом, лямбда-решение является правильным. Смотрите этот комментарий для дополнительной информации.
Других решений пока нет …