Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr vs template for compile-time maths functions?

I'm quite confused with the new keyword constexpr of C++2011. I would like to know where to use constexpr and where to use templates metaprogramming when I code compile-time functions (especially maths functions). For example if we take an integer pow function :

// 1 : 
template <int N> inline double tpow(double x)
{
    return x*tpow<N-1>(x);
}
template <> inline double tpow<0>(double x)
{
    return 1.0;
}

// 2 :
constexpr double cpow(double x, int N)
{
    return (N>0) ? (x*cpow(x, N-1)) : (1.0);
}

// 3 :
template <int N> constexpr double tcpow(double x)
{
    return x*tcpow<N-1>(x);
}
template <> constexpr double tcpow<0>(double x)
{
    return 1.0;
}

Are the 2nd and 3rd functions equivalent ? What is the best solution ? Does it produce the same result :

  • if x is known at compile-time
  • if x is not known at compile-time

When to use constexpr and when to use template metaprogramming ?

EDIT 1 : code modified to include specialization for templates

like image 340
Vincent Avatar asked Sep 02 '12 04:09

Vincent


1 Answers

I probably shouldn't be answering a template metaprogramming question this late. But, here I go.

Firstly, constexpr isn't implemented in Visual Studio 2012. If you want to develop for windows, forget about it. I know, it sucks, I hate Microsoft for not including it.

With that out of the way, there's lots of things you can declare as constant, but they aren't really "constant" in terms of "you can work with them at compile time." For instance:

const int foo[5] = { 2, 5, 1, 9, 4 };
const int bar = foo[foo[2]]; // Fail!

You'd think you could read from that at compile time, right? Nope. But you can if you make it a constexpr.

constexpr int foo[5] = { 2, 5, 1, 9, 4 };
constexpr int bar = foo[foo[2]]; // Woohoo!

Constexpr's are really good for "constant propagation" optimization. What that means is if you have a variable X, that is declared at compile time based on some condition (perhaps metaprogramming), if it is a constexpr then the compiler knows it can "safely" use it when doing optimization to, say, remove instructions like a = (X * y); and replace them with a = 0; if X evaluated to 0 (and other conditions are met).

Obviously this is great because for many mathematical functions, constant propagation can give you an easy (to use) premature optimization.

Their main use, other than rather esoteric things (such as enabling me to write a compile-time byte-code interpreter a lot easier), is to be able to make "functions" or classes that can be called and used both at compile-time and at runtime.

Basically they just sort of fill a hole in C++03 and help with optimization by the compiler.

So which of your 3 is "best"?

2 can be called at run-time, whereas the others are compile-time only. That's pretty sweet.

There's a bit more to it. Wikipedia gives you a very basic summary of "constexpr allows this," but template metaprogramming can be complicated. Constexpr makes parts of it a lot easier. I wish I had a clear example for you other than say, reading from an array.

A good mathematical example, I suppose, would be if you wanted to implement a user-defined complex number class. It would be an order of magnitude more complex to code that with only template metaprogramming and no constexpr.

So when should you not use constexpr? Honestly, constexpr is basically "const except MORE CONST." You can generally use it anywhere you'd use const, with a few caveats such as how when called during runtime a function will act non-const if its input isn't const.

Um. OK, that's all for now. I'm too overtired to say more. I hope i was helpful, feel free to downvote me if I wasn't and I'll delete this.

like image 145
std''OrgnlDave Avatar answered Jan 01 '23 17:01

std''OrgnlDave