When should I use the constexpr feature in C ++ 11?

In my opinion, having the "always return 5" function is destroying or diluting the meaning of "call function". There has to be a reason, or it needs this capability, or it won't appear in C ++ 11. Why is that?

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

In my opinion, if I write a function that returns a literal value and I do a code review, someone will tell me that I should declare a constant value instead of writing return 5.

#1 building

From Stroustrup's speech in "Going Native 2012":

template<int M, int K, int S> struct Unit { // a unit in the MKS system
       enum { m=M, kg=K, s=S };
};

template<typename Unit> // a magnitude with a unit 
struct Value {
       double val;   // the magnitude 
       explicit Value(double d) : val(d) {} // construct a Value from a double 
};

using Speed = Value<Unit<1,0,-1>>;  // meters/second type
using Acceleration = Value<Unit<1,0,-2>>;  // meters/second/second type
using Second = Unit<0,0,1>;  // unit: sec
using Second2 = Unit<0,0,2>; // unit: second*second 

constexpr Value<Second> operator"" s(long double d)
   // a f-p literal suffixed by 's'
{
  return Value<Second> (d);  
}   

constexpr Value<Second2> operator"" s2(long double d)
  // a f-p literal  suffixed by 's2' 
{
  return Value<Second2> (d); 
}

Speed sp1 = 100m/9.8s; // very fast for a human 
Speed sp2 = 100m/9.8s2; // error (m/s2 is acceleration)  
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit) 
Acceleration acc = sp1/0.5s; // too fast for a human

#2 building

Just started to switch the project to c ++ 11 and encountered the perfect situation of constexpr, which cleans up alternative ways to do the same. The key point here is that the function can only be put into the array size declaration when constexpr is declared. In many cases, I can see that this is very useful for the code areas I'm involved in.

constexpr size_t GetMaxIPV4StringLength()
{
    return ( sizeof( "255.255.255.255" ) );
}

void SomeIPFunction()
{
    char szIPAddress[ GetMaxIPV4StringLength() ];
    SomeIPGetFunction( szIPAddress );
}

#3 building

introduce

constexpr is not a way to tell an implementation that something can be evaluated in a context that requires constant expression; conforming implementations have been able to prove this before C ++ 11.

What the implementation can't prove is the intent of a piece of code:

  • What do developers want to express with this entity?
  • Should we blindly allow code to be used in constant expressions just because it happens to work?

Without constexpr, what would the world be like?

Suppose you are developing a library and realize that you want to be able to calculate the sum of each integer in the interval (0,N).

int f (int n) {
  return n > 0 ? n + f (n-1) : n;
}

Lack of intention

If the parameters passed are known during the conversion, the compiler can easily prove that the above functions are callable in a constant expression. But you don't claim it's an intention - the opposite is true.

Now someone else appears, reads your function and does the same analysis as the compiler: "Oh, this function can be used for constant expression!" , and write the following code.

T arr[f(10)]; // freakin' magic

optimization

As an "awesome" library developer, you decide that f should cache the results when it calls; who wants to compute the same set of values over and over again?

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];
}

Result

By introducing your silly optimizations, you've just broken every use of a function that happens to be in the context of a constant expression.

You never promised that this function could be used for constant expressions, and without constexpr, there would be no way to provide such a promise.

So, why do we need constexpr?

The main use of constexpr is to declare intent.

If the entity is not marked constexpr - it was never intended for constant expressions; even if it is, we rely on the compiler to diagnose such a context (because it ignores our intent).

#4 building

From what I read, the requirement for constexpr comes from a problem in metaprogramming. The trade class may have constants expressed as functions, think: numeric_limits:: max(). With constexpr, these types of functions can be used in metaprogramming, as array boundaries, and so on.

Another example is, for a class interface, you might want derived types to define their own constants for certain operations.

Editor:

After exploring SO, it seems that others have already proposed some Probably related to constexprs Example .

#5 building

Suppose it does something more complicated.

constexpr int MeaningOfLife ( int a, int b ) { return a * b; }

const int meaningOfLife = MeaningOfLife( 6, 7 );

Now, you can evaluate something as a constant while maintaining good readability and allowing for slightly more complex processing than just setting the constant to a number.

It basically helps with maintainability because it's becoming more obvious. Take max (a, b) as an example:

template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }

It's a very simple choice, but it does mean that if max is called with a constant value, it will be evaluated explicitly at compile time, not at run time.

Another good example is the DegreesToRadians function. Everyone has found that a degree is easier to read than a radian. Although you may know that 180 degrees is radians, write it more clearly as follows:

const float oneeighty = DegreesToRadians( 180.0f );

Here's a lot of good news:

http://en.cppreference.com/w/cpp/language/constexpr

Posted on Tue, 17 Mar 2020 03:57:05 -0700 by keyont