9

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?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
pezcode
  • 5,490
  • 2
  • 24
  • 37
  • 1
    Why would you want to do that? The `factorial` function doesn't accept any parameters. What's the benefit of `factorial(5)` over the correct `factorial<5>()`? – Cody Gray - on strike Jan 12 '12 at 15:01
  • @CodyGray: I think that the idea would be to create a generic `factorial` function able to calculate the result at compile time if it's given a compile-time-known expression, but also able to calculate it at runtime if the input is a normal variable. – Matteo Italia Jan 12 '12 at 15:02
  • @Matteo: So far as I understand, nothing is being calculated at runtime since the expression is declared `constexpr`. And anyway, I don't understand why there'd be any problem passing a "normal variable" instead of the 5 using the standard syntax. The question here is "how can I avoid typing angle brackets", and I don't understand the motivation. – Cody Gray - on strike Jan 12 '12 at 15:05
  • I want to avoid having to change lots of code. Also, I find it to be more idiomatic, but that's something I can easily pass on ;) – pezcode Jan 12 '12 at 15:13
  • @CodyGray: `constexpr` works just like @Matteo said: if all arguments in a `constexpr` function call are compile-time constants, the call will be evaluated at compile time, otherwise, it's executed at runtime like any other function. – JohannesD Jan 12 '12 at 23:28

7 Answers7

9

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.

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • 1
    I didn't know constexpr works opt-out like that. Sadly, the code is just an example and I need a fixed-size array inside the actual function, so I need a template with the array size as a parameter. – pezcode Jan 12 '12 at 15:29
  • Perhaps build an array of function pointers to the templated functions at compile time, and look them up at runtime? `factorial[3](.. other args ..)` – Aaron McDaid Jan 12 '12 at 15:52
  • @pezcode: change factorial to a macro that calls the template code, Aaron: youd be better of building an array of the results – Mooing Duck Jan 12 '12 at 15:53
  • @MooingDuck, yep, but see pezcode's comment on this answer - I think the real application is more complicated than this. I get the impression that some args are to be these templated-ints, whereas their may be other parameters also. – Aaron McDaid Jan 12 '12 at 16:48
  • If you can mark every function and constructor as `constexpr` without any negative consequences, it (again) raises the question of why this isn't the default for C++11 compilers... I guess it's the sacred back-compat problem again. – Cody Gray - on strike Jan 13 '12 at 00:18
  • I'm no expert @CodyGray, but I think the only downside with default constexpr for everything is that the compilation might be much slower or perhaps even be stuck in an infinite loop. *PS:* And maybe there are some compatibility issues as you say, certain software might assume they're dealing with 'real' functions. – Aaron McDaid Jan 13 '12 at 00:39
  • A function marked as `constexpr` is limited in what you can do inside of it to limit the burden on compiler-writers to be able to compute it at compile time. You mark functions with `constexpr` to tell the compiler that it should complain if you don't meet those restrictions so that your code will be portable to other compilers. – David Stone Jun 29 '13 at 22:12
7

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?

jalf
  • 243,077
  • 51
  • 345
  • 550
  • Pretty much, I have a huge code base with calls to several functions which I rewrote as a template. Replacing the names is trivial, but turning all `f(2, 3, "abc", true)` into `f<2, 3, true>("abc")` is a little bit trickier. – pezcode Jan 12 '12 at 15:08
  • 3
    How about a macro to convert `f(2, 3, "abc", true)` into `f_impl<2, 3, true>("abc")`? – Aaron McDaid Jul 02 '13 at 15:36
1

Use an evil macro:

#define factorial(X) factorial<X>()
jxh
  • 69,070
  • 8
  • 110
  • 193
1

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.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
1

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.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
0

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).

Predelnik
  • 5,066
  • 2
  • 24
  • 36
0

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;
}
BЈовић
  • 62,405
  • 41
  • 173
  • 273