Published on

Do you really know constexpr?

Authors

Sneakiness of constexpr

I am assuming in your C++ journey, you might have come across constexpr for sure. While on the surface it looks like you are defining a constant expression, the questions is are you really doing that?

The answer is yes and no.

Yes how?

If you are defining a variable as a constant, you are telling the compiler that the value of this variable is known at compile time (linking time to be accurate) and is constant:

constexpr int a = 10; // Valid use
constexpr int b; // Will give a compiler error since value of b is unknown

In easy terms, every constexpr variable is const but not vice-versa.

No how?

The real sneakiness of constexpr comes into play with functions. It is not guaranteed that a function defined as constexpr will indeed be evaluated at compile time. Here are two scenarios:

All arguments of the function are available at compile time

The function will hence be computed at compile time. Example:

constexpr int arg1 = 10;
constexpr int arg2 = 20;

constexpr int sum(int a, int b){
    return a + b;
}

sum(arg1,arg2); // Evaluated at compile time

int arr[sum(arg1,arg2)]; // Will compile fine

One or more arguments of the function are not available at compile time

The function will be computed at runtime. Example:

const int arg1;
constexpr int arg2 = 20;

constexpr int sum(int a, int b){
    return a + b;
}

sum(arg1,arg2); // Evaluated at run time

int arr[sum(arg1,arg2)]; // Will not compile

You can achieve two functionalities with this trick. You can make your function behave differently for different arguments.

Wrapping up

constexpr functions only work with literal types as arguments and return types. Literal types include every built-in type except for void. It can also work with any user-defined types as well only if the user type has a constexpr constructors and member functions.

Example:

class Database{
  std::string _fileName;
  public:
  constexpr Database(std::string fileName): _fileName(fileName){};
  constexpr std::string serialize(){
      // some logic here
  }
};

constexpr Database db("someFileName");
constexpr serializationResult = db.serialize();

Note

Remember I said that constexpr functions do not work with void. Since C++14, setters are allowed to have a void return type as constexpr expressions.

Also, be very careful in defining any epxressions as constexpr. Once you do that, it's very difficult to revert back because your clients will suddenly start getting compile time errors.

Hope this helps.

Signing out,

-G