c++ - Los argumentos literales definidos por el usuario no son constexpr?
c++11 user-defined-literals (5)
Estoy probando literales definidos por el usuario. Quiero hacer que _fac
devuelva el factorial del número.
Hacer que llame a una función constexpr
funciona, sin embargo, no me permite hacerlo con plantillas, ya que el compilador se queja de que los argumentos no son ni pueden ser constexpr
.
Estoy confundido por esto, ¿no son literales expresiones constantes? El 5
en 5_fac
es siempre un literal que puede evaluarse durante el tiempo de compilación, ¿por qué no puedo usarlo como tal?
Primer método:
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
}
Segundo método:
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
}
@Pubby. La manera fácil de digerir el paquete de parámetros de caracteres no tipográficos es incluirlo en una lista de inicializadores para una cadena. Luego puedes usar atoi, atof, etc:
#include <iostream>
template<char... Chars>
int
operator "" _suffix()
{
const char str[]{Chars..., ''/0''};
return atoi(str);
}
int
main()
{
std::cout << 12345_suffix << std::endl;
}
Recuerde añadir un carácter nulo para las funciones de estilo C.
Así es como terminé haciéndolo:
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;
}
Muchísimas gracias a KerrekSB!
No sé si hay una forma mejor en C ++ 11 de hacer esto que la respuesta aceptada actual, pero con constexpr
relajado en C ++ 14, simplemente puede escribir el código "normal":
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, "!");
Para hacer uso de constexpr con literales definidos por el usuario, aparentemente tienes que usar una plantilla variad. Echa un vistazo a la segunda lista en el artículo de wikipedia para ver un ejemplo.
Puede que esté equivocado, pero creo que las funciones constexpr también pueden llamarse con argumentos no constantes (en cuyo caso no dan una expresión constante y se evalúan en tiempo de ejecución). Lo que no funcionaría bien con argumentos de plantilla que no son de tipo.