Use constexpr whenever possible.

Use constexpr whenever possible.

·

4 min read

If there were an award for the most confusing new word in C++11, constexpr would probably win it. When applied to objects, it’s essentially a beefed-up form of const, but when applied to functions, it has a quite different meaning. Cutting through the confusion is worth the trouble because when constexpr corresponds to what you want to express, you want to use it. Conceptually, constexpr indicates a value that's not only const but also known during compilation. constexpr function return type is const whether in the compilation time or runtime.

constexpr objects

they are const, but they are known during compilation time leaving a chance to be but in the read-only memory while giving you such privilege is of considerable importance. the size of the array, the enum values and template arguments are all needed to be const, const in return doesn't guarantee this privilege because they may be assigned to the value determined in the runtime.

int sz; // non-constexpr var 
constexpr auto arraySize1=sz; // error sz'value not knon 
                              // at compilation time.
std::array<int,sz>data1;  // error the same prob; 
constexpr auto arraySize2=10 // fine, 10 is a compiler time const. 
std::array<int,arraySize2>data2; // also fine as arraySize2 is constexpres.

while the claim about const they don't guarantee the same protection.

int sz; // non-const var

const auto arraySize=sz;    // fine arraysize is a const copy of sz

std::array<int,arraSize>data; // error arraySize'value not known
                              // at compilation time.

Simply if all constexprs are const but not all const objects are constexpr. if you want to make sure to use the right tool to determine the value of the const values that require such kinds of vars then constexpr not const.

Constexpr with functions

they are really interesting when they are called with values known at compile time they produce const at compile time and when they are called with values known at runtime they produce const values at runtime. we can declare two points about when to use the constexpr in real life.

  1. constexpr functions are used when you want a guarantee of a compile-time const like declaring an array with size. passing arguments that are not determined during compilation the code will be rejected.

  2. when it is used in another context where the return of the function doesn't need to be determined during compilation then the function will act as a normal function returning a const value.

//
decltype(auto) func(int x){return x*2;}; // the return of the function is int 
std::array<int,func(3)> array; //error as the return of the func is not determined during compilation time 
// the best solution is to use constexpr
// this appracc

the best solution is to use constexpr, but this is not quite right in total- seems number 2 in the above two explanations. the only case to use func at compiler time is to make sure to pass compile-time values.

another way to use constexpr function

used in the constructor and other functions like getters. there is actually a trick that could be much helpful.

 class Point {
   public:
constexpr Point(double xVal = 0, double yVal = 0) noexcept : x(xVal), y(yVal)
{}
constexpr double xValue() const noexcept { return x; } constexpr double yValue() const noexcept { return y; }
     void setX(double newX) noexcept { x = newX; }
     void setY(double newY) noexcept { y = newY; }
constexpr Point midpoint(const Point& p1,const Point& p2{
return { (p1.xValue() + p2.xValue()) / 2, // call constexpr
(p1.yValue() + p2.yValue()) / 2 }; // member funcs
}
   private:
     double x, y;
};

constexpr Point p1(9.4,27.7),p2(10,6.2);
constexpr auto mid=midpoint(p1,p2); //result in a compile time value

it sounds exciting, as the object mid though its initializer involves calls to getters and non-member func but it was created in read only memory. thus yiu can explicitly use mid.xValue() * 2 in a compile time const value. the line between the runtime and compiletime starts to vanish making the software faster. yet the setters in C++11 can not be declared as constexpr for two reasons

  1. the setters change the value they operates on

  2. they have void function whivh is not literal type in c++11.

but now with c++14 all the two above restrictions vanishes.


constexpr void setX(double newX) noexcept { x = newX; }
constexpr void setY(double newY) noexcept { y = newY; }

now what can we do when we know the setters can be known during compilation time. well, let's take an example of a point created and it's value known at compiler time as well.

constexpr Point reflec(const Point&p)nonexcept{
auto result;
result.seX(-p.xValue());
result.setY(-p.yValue());
return result; 
}

// for the client 
 constexpr auto reflectedMid =         // reflectedMid's value is
     reflection(mid);                    // (-19.1 -16.5) and known
                                         // during compilation

the final words is to use constexpre whenever you need to use const and to use it when the value needed to be at compile time.

Things to Remember

  • constexpr objects are const and are initialized with values known during compilation.

  • constexpr functions can produce compile-time results when called with arguments whose values are known during compilation.

  • constexpr objects and functions may be used in a wider range of contexts than non-constexpr objects and functions.

  • constexpr is part of an object’s or function’s interface.