Определенные пользователем литералы не являются constexpr?
Я тестирую пользовательские литералы. Я хочу сделать _fac
возврат факториала числа.
Если он вызывает функцию constexpr
, однако он не позволяет мне делать это с помощью шаблонов, поскольку компилятор жалуется, что аргументы не являются и не могут быть constexpr
.
Я смущен этим - не являются ли литералы постоянными выражениями? 5
in 5_fac
всегда является литералом, который можно оценить во время компиляции, поэтому почему я не могу использовать его как таковой?
Первый метод:
constexpr int factorial_function(int x) {
return (x > 0) ? x * factorial_function(x - 1) : 1;
}
constexpr int operator "" _fac(unsigned long long x) {
return factorial_function(x); // this works
}
Второй метод:
template <int N> struct factorial_template {
static const unsigned int value = N * factorial_template<N - 1>::value;
};
template <> struct factorial_template<0> {
static const unsigned int value = 1;
};
constexpr int operator "" _fac(unsigned long long x) {
return factorial_template<x>::value; // doesn't work - x is not a constexpr
}
Ответы
Ответ 1
Вот как я это сделал:
template <typename t>
constexpr t pow(t base, int exp) {
return (exp > 0) ? base * pow(base, exp-1) : 1;
};
template <char...> struct literal;
template <> struct literal<> {
static const unsigned int to_int = 0;
};
template <char c, char ...cv> struct literal<c, cv...> {
static const unsigned int to_int = (c - '0') * pow(10, sizeof...(cv)) + literal<cv...>::to_int;
};
template <int N> struct factorial {
static const unsigned int value = N * factorial<N - 1>::value;
};
template <> struct factorial<0> {
static const unsigned int value = 1;
};
template <char ...cv>
constexpr unsigned int operator "" _fac()
{
return factorial<literal<cv...>::to_int>::value;
}
Огромное спасибо KerrekSB!
Ответ 2
Я не знаю, есть ли лучший способ в С++ 11 сделать это, чем текущий принятый ответ, но с расслабленным constexpr
в С++ 14 вы можете просто написать "обычный" код:
constexpr unsigned long long int operator "" _fac(unsigned long long int x) {
unsigned long long int result = 1;
for (; x >= 2; --x) {
result *= x;
}
return result;
}
static_assert(5_fac == 120, "!");
Ответ 3
Чтобы использовать constexpr с определенными пользователем литералами, вам, видимо, придется использовать вариационный шаблон. Взгляните на второй список в статье в википедии для примера.
Ответ 4
Я могу ошибаться, но я думаю, что функции constexpr также могут вызываться с непостоянными аргументами (в этом случае они не дают постоянного выражения и оцениваются во время выполнения). Что бы не сработало с аргументами шаблона не-типа.
Ответ 5
@Pubby. Легкий способ переварить пакет параметров char non-type - это закрепить его в списке инициализаторов для строки. Затем вы можете использовать atoi, atof и т.д.:
#include <iostream>
template<char... Chars>
int
operator "" _suffix()
{
const char str[]{Chars..., '\0'};
return atoi(str);
}
int
main()
{
std::cout << 12345_suffix << std::endl;
}
Не забудьте применить нулевой символ для функций стиля C.