Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constexpr, templates and array size

I would like to pass template argument to function call and the return value use as size of an array ie

constexpr int myPow(int a, int b){
  int result = 1;
  for(int i=0;i<b;i++)
    result *= a;
  return result;
}

template <int N>
class testClass{
public:
  testClass(){}
  int array[myPow(2,N)];
};

int main(){
  testClass<3> A;
  return 0;
}

compiler errors:

~ $ g++-4.6 test.cpp -std=gnu++0x
test.cpp: In function ‘constexpr int myPow(int, int)’:
test.cpp:6:1: error: body of constexpr function ‘constexpr int myPow(int, int)’ not a return-statement
test.cpp: At global scope:
test.cpp:12:23: error: array bound is not an integer constant before ‘]’ token

Any idea how to get around this?

like image 377
tom Avatar asked Dec 05 '22 10:12

tom


2 Answers

In C++11, a constexpr function can only contain a return statement (see here for the full details), so your myPow function is not constexpr-compliant (because it contains a for loop).

You can use this metafunction to compute the integer power at compile-time:

template <int N, typename Type> 
constexpr Type pow(const Type& x) 
{
    return (N > 1) ? (x*pow<(N-1)*(N > 1)>(x)) 
                   : ((N < 0) ? (static_cast<Type>(1)/pow<(-N)*(N < 0)>(x)) 
                              : ((N == 1) ? (x) 
                                          : (static_cast<Type>(1))));
}

If you want to compute 2^N, you can type:

pow<N>(2)

Note 1: this metafunction is very generic and also work for negative integers and floating-point types, so you can type : pow<-3>(3.14)

Note 2: the multiplication by N>1 or N<0 in the template are here to block infinite recursivity and force the template parameter to be equal to zero when the branch is not relevant. This could be done with template specialization, but the technique used here allow to write a single function.

like image 50
Vincent Avatar answered Dec 07 '22 01:12

Vincent


In C++11, constexpr functions are quite restricted, and your code doesn't comply with the restrictions (you can't declare variables, mutate local state, nor use most forms of statements -- including loops). However, C++1y removes most of the restrictions, and Clang 3.3 accepts your original code sample in its -std=c++1y mode.

If you need the code to work in C++11 mode, you can rewrite it to sidestep the constexpr restrictions:

constexpr int myPow(int a, int b) {
  return b ? a * myPow(a, b - 1) : 1;
}
like image 34
Richard Smith Avatar answered Dec 07 '22 01:12

Richard Smith