It seems to me that having a "function that always returns 5" is breaking or diluting the meaning of "calling a function". There must be a reason, or a need for this capability or it wouldn't be in C++11. Why is it there?
// preprocessor.
#define MEANING_OF_LIFE 42
// constants:
const int MeaningOfLife = 42;
// constexpr-function:
constexpr int MeaningOfLife () { return 42; }
It seems to me that if I wrote a function that return a literal value, and I came up to a code-review, someone would tell me, I should then, declare a constant value instead of writing return 5.
Yes. I believe putting such const ness is always a good practice wherever you can. For example in your class if a given method is not modifying any member then you always tend to put a const keyword in the end.
In Conclusion. constexpr is an effective tool for ensuring compile-time evaluation of function calls, objects and variables. Compile-time evaluation of expressions often leads to more efficient code and enables the compiler to store the result in the system's ROM.
A constexpr function that is eligible to be evaluated at compile-time will only be evaluated at compile-time if the return value is used where a constant expression is required. Otherwise, compile-time evaluation is not guaranteed.
Suppose it does something a little more complicated.
constexpr int MeaningOfLife ( int a, int b ) { return a * b; } const int meaningOfLife = MeaningOfLife( 6, 7 );
Now you have something that can be evaluated down to a constant while maintaining good readability and allowing slightly more complex processing than just setting a constant to a number.
It basically provides a good aid to maintainability as it becomes more obvious what you are doing. Take max( a, b )
for example:
template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }
Its a pretty simple choice there but it does mean that if you call max
with constant values it is explicitly calculated at compile time and not at runtime.
Another good example would be a DegreesToRadians
function. Everyone finds degrees easier to read than radians. While you may know that 180 degrees is 3.14159265 (Pi) in radians it is much clearer written as follows:
const float oneeighty = DegreesToRadians( 180.0f );
Lots of good info here:
http://en.cppreference.com/w/cpp/language/constexpr
constexpr
was not introduced as a way to tell the implementation that something can be evaluated in a context which requires a constant-expression; conforming implementations has been able to prove this prior to C++11.
Something an implementation cannot prove is the intent of a certain piece of code:
constexpr
?Let's say you are developing a library and realize that you want to be able to calculate the sum of every integer in the interval (0,N]
.
int f (int n) {
return n > 0 ? n + f (n-1) : n;
}
A compiler can easily prove that the above function is callable in a constant-expression if the argument passed is known during translation; but you have not declared this as an intent - it just happened to be the case.
Now someone else comes along, reads your function, does the same analysis as the compiler; "Oh, this function is usable in a constant-expression!", and writes the following piece of code.
T arr[f(10)]; // freakin' magic
You, as an "awesome" library developer, decide that f
should cache the result when being invoked; who would want to calculate the same set of values over and over?
int func (int n) {
static std::map<int, int> _cached;
if (_cached.find (n) == _cached.end ())
_cached[n] = n > 0 ? n + func (n-1) : n;
return _cached[n];
}
By introducing your silly optimization, you just broke every usage of your function that happened to be in a context where a constant-expression was required.
You never promised that the function was usable in a constant-expression, and without constexpr
there would be no way of providing such promise.
constexpr
?The primary usage of constexpr is to declare intent.
If an entity isn't marked as constexpr
- it was never intended to be used in a constant-expression; and even if it is, we rely on the compiler to diagnose such context (because it disregards our intent).
Take std::numeric_limits<T>::max()
: for whatever reason, this is a method. constexpr
would be beneficial here.
Another example: you want to declare a C-array (or a std::array
) that is as big as another array. The way to do this at the moment is like so:
int x[10];
int y[sizeof x / sizeof x[0]];
But wouldn’t it be better to be able to write:
int y[size_of(x)];
Thanks to constexpr
, you can:
template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
return N;
}
constexpr
functions are really nice and a great addition to c++. However, you are right in that most of the problems it solves can be inelegantly worked around with macros.
However, one of the uses of constexpr
has no C++03 equivalent, typed constants.
// This is bad for obvious reasons.
#define ONE 1;
// This works most of the time but isn't fully typed.
enum { TWO = 2 };
// This doesn't compile
enum { pi = 3.1415f };
// This is a file local lvalue masquerading as a global
// rvalue. It works most of the time. But May subtly break
// with static initialization order issues, eg pi = 0 for some files.
static const float pi = 3.1415f;
// This is a true constant rvalue
constexpr float pi = 3.1415f;
// Haven't you always wanted to do this?
// constexpr std::string awesome = "oh yeah!!!";
// UPDATE: sadly std::string lacks a constexpr ctor
struct A
{
static const int four = 4;
static const int five = 5;
constexpr int six = 6;
};
int main()
{
&A::four; // linker error
&A::six; // compiler error
// EXTREMELY subtle linker error
int i = rand()? A::four: A::five;
// It not safe use static const class variables with the ternary operator!
}
//Adding this to any cpp file would fix the linker error.
//int A::four;
//int A::six;
From what I've read, the need for constexpr comes from an issue in metaprogramming. Trait classes may have constants represented as functions, think: numeric_limits::max(). With constexpr, those types of functions can be used in metaprogramming, or as array bounds, etc etc.
Another example off of the top of my head would be that for class interfaces, you may want derived types define their own constants for some operation.
Edit:
After poking around on SO, it looks like others have come up with some examples of what might be possible with constexprs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With