C++11 – Part 7: Constant Expressions

Constant expressions are an interesting area of the C++11 standard. They really do not really allow you to achieve anything new with the language, but they do allow it to be achieved in a more simple fashion and extend the list of things that can be done at compile time making everything faster! Lets start off with a simple example of the sort of issues that needed solved:

std::size_t three() { return 3; }

A nice simple function… What might we do with such a function? How about:

const std::size_t i = three();

This is perfectly legal in C++03, but the assignment is evaluated at runtime. That means that many optimizations involving i might be missed. Another example:

int i[three()];

This is not legal C++03, although it may work depending on your compiler. Moving on to an example that definitely breaks!

template<size_t T> struct S {};
S<three()> s;

I do not know of a compiler that accepted that. You may think of these as a bit of a contrived example, but consider that things like std::numeric_limits<int>::max() are functions and running into these sorts of issues was not uncommon. There was a work around in the last case in terms of using C-style macros (e.g. INT_MAX) at the cost of losing type safety, or declaring a constant variable as in the first usage example above with all its disadvantages.

C++11 introduces the keyword constexpr to work around these issues. This is a guarantee that result of the function is a constant, provided the function is passed constant parameters. So, the function above can be declared as

constexpr std::size_t three() { return 3; }

Now the compiler knows that this function produces a constant so it is evaluated at compile time.

There are a few restrictions on what functions can be declared as constexpr. Essentially, it needs to a function that only contains single return statement that uses only literals, constexpr variables (defined later) and other constexpr functions. There are some other minor allowances for what the function may have such as (some) typedefs and using statements, but thinking of it as a one line function is probably best. Some recursion is allowed (compiler dependent but must be up to 512 levels), so calculating a factorial at compile time can done using:

constexpr int f(int x) { return x > 1 ? x * f(x - 1) : 1; }

Evaluating a factorial at compile time could previously be done by template metaprogramming, but that has some overhead and is much more difficult to specify. One thing to note is that the parameter of the factorial function does not need to be a constexpr, but the result will then be calculated at runtime.

This brings me to the difference between const variables and constexpr variables. Variables declared as constexpr are const but with some more restrictions. They must be immediately constructed (so nothing like extern constexpr int i;) and can only be constructed using literals and other constexpr variables and functions.

Now we know that every object that is used in a constexpr function must itself be a constexpr, but what about objects that have constructors? They can be used provided that their constructor is a constexpr so that it has only member initializations (using only other constexpr constructors if needed…). For example:

class Foo
  constexpr Foo(int foo) : _foo(foo) {}
  constexpr foo() { return _foo; }
  int _foo;

With that class, a variable of type Foo can be declared as a constexpr. Also the Foo.foo() function is a constexpr and thus can be used anyway one is needed.

3 thoughts on “C++11 – Part 7: Constant Expressions

  1. This should be categorized in C++11 as the others so it can be followed easily.

  2. Keep up the good work. I learnt a lot about C++11 from 7 articles.