Deduce non-type template parameter
-
15-04-2021 - |
Pergunta
Is it possible to deduce a non-type template parameter from a template function parameter?
Consider this simple template:
template <int N> constexpr int factorial()
{
return N * factorial<N - 1>();
}
template <> constexpr int factorial<0>()
{
return 1;
}
template <> constexpr int factorial<1>()
{
return 1;
}
I would like to be able to change factorial
so that I can alternatively call it like this:
factorial(5);
and let the compiler figure out the value of N at compile time. Is this possible? Maybe with some fancy C++11 addition?
Solução
Can't be done, unless you have a time machine.
The parameter to the function is handled at runtime. Yes, in your case it's a literal constant, but that's a special case.
In function definitions, the parameter types are fixed at compile-time (and thus, can be used to deduce template parameters), but parameter values are only fixed at runtime.
Why do you need this? Is it just so you don't have to type the <>
's?
Outras dicas
Your current code would normally be written as follows, I believe:
constexpr factorial (int n)
{
return n > 0 ? n * factorial( n - 1 ) : 1;
}
If you call it with a constant-expression, such as factorial(5)
, then all the compiler magic will come into play. But if you do int a = 3; factorial(a)
, then I think it will fall back on a conventional function - i.e. it won't have built a lookup table of pre-computed answers.
In general, you should mark every function and constructor as constexpr
if you can. You lose nothing, the compiler will treat it as a normal function if necessary.
I don't think you can do that; the only way you could do that would be having a constexpr
function parameter that would be passed then as the template
parameter for the template version of factorial
, but constexpr
function parameters are not admitted.
No, you can't do that. Template arguments can only be deduced from the type of function argument, not the value, which will not in general be known at compile time.
Of course, you could rewrite factorial
as a non-template constexpr
function; then it would be evaluated at compile time if the argument is known then.
No, it is not possible, unless you want to create a huge switch statement :
int getFactorial( const int v )
{
switch ( v )
{
case 1 : return factorial<1>();
case 2 : return factorial<2>();
//etc
default:
;
}
return 0;
}
Use an evil macro:
#define factorial(X) factorial<X>()
One possible workaround solution for this kind of problem is to utilize some struct
like this:
template <int X>
struct constant {
operator int () { return X; }
};
Then we can have overload on constant<X>
like this:
template <int X>
void f (constant<X>)
Utilizing value of X
at compile-time and runtime overloads f (int)
. The annoying part is that it requires some discipline on the call site since every literal constant has to be wrapped as constant<X> {}
(with macro or something like this).