3

I wrote this function

    template <typename T>
    double norm(const T & v, const int p) {
        return v.template lpNorm<p>();
    }

but it doesn't work and gives the error:

 error: 'p' is not a constant expression
             return v.template lpNorm<p>();
                                         ^

I seem to think that the compiler expects p to be known at compile time whereas my p is dynamic.

Possibly related:

Why is this not a constant expression?

Non-type template argument is not a constant expression

How can I fix this?

Community
  • 1
  • 1

3 Answers3

3

You cant.

Templates are resolved at compile time. This means for each value of p in the line lpNorm<p>() you will generate code specific to that template. If p can be any value then you need to generate code for every possible value which is not a good idea, the compiler is telling you this.

p must be known at compile time which means you need to do something like this:

template <int p, typename T>
double norm(const T & v) {

Then specialize for any value of p that you might expect to receive.

If p truely is dynamic, then you need a run-time solution, not a compile time solution, so it is likely that you want this:

lpNorm(p);

(you will obviously need to redefine how lpNorm works)

Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175
3

Templates are resolved at compile-time. And because of that they can't be run-time variables. And despite the const in the argument declaration p is still a run-time variable.

The const in the argument declaration simply means that the function can not change the argument value. It's still possible to call your function with a normal non-constant variable.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
3

You can partially do it with a limited range of p. For lpNorm operation it is usually enough.

You've already known that you need a compile-time constant as the template parameter. However as p is usually used in a small range (0,1,2,inf), you could use the following trick to make it work for a small sub-set of commonly used int values.

template<typename T>
double norm(const T & v, const int p) {
  switch (p) {
  case 0:
    return v.template lpNorm<0>();
    break;
  case 1:
    return v.template lpNorm<1>();
    break;
  case 2:
    return v.template lpNorm<2>();
    break;
  case Eigen::Infinity:
    return v.template lpNorm<Eigen::Infinity>();
    break;
  default:
    break;
  }
}
kangshiyin
  • 9,681
  • 1
  • 17
  • 29
  • Just out of curiosity, could we generate the equivalent of the switch using some template magic? – kloffy Jul 15 '16 at 03:55
  • To be a bit more specific, I mean something that will take a generic functor and `std::integer_sequence` of possible values at compile-time, and generate the necessary plumbing to select based on run-time values. – kloffy Jul 15 '16 at 04:14
  • @lisyarus Precisely. That will be very handy to have around, thanks! – kloffy Jul 15 '16 at 19:58