4

Let's take a simple SFINAE templating example

#include <iostream>

template <typename T>
struct has_typedef_foobar {
    // Types "yes" and "no" are guaranteed to have different sizes,
    // specifically sizeof(yes) == 1 and sizeof(no) == 2.
    typedef char yes[1];
    typedef char no[2];

    template <typename C>
    static yes& test(typename C::foobar*);

    template <typename>
    static no& test(...);

    // If the "sizeof" of the result of calling test<T>(0) would be equal to sizeof(yes),
    // the first overload worked and T has a nested type named foobar.
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

struct foo {    
    typedef float foobar;
};

int main() {
    std::cout << std::boolalpha;
    std::cout << has_typedef_foobar<int>::value << std::endl;
    std::cout << has_typedef_foobar<foo>::value << std::endl;
}

The bool value has not been declared constexpr but it still gets its value on compile time. What then is the use of constexpr and why do simple static variables get their values at compile time? How can I tell which static variables get their value at compile time?

Also how can I tell which values will get evaluated at compile time and which will not? Will using constexpr guarantee compile time evaluation? If not how can I know which will happen (compile time or run time)?

  • You should add the relevant code from the link, in case it changes in the future – Tas Aug 27 '15 at 04:32

2 Answers2

12

One of the key things that constexpr gives you that was not really possible to achieve before is the ability to call a function in a context that requires a compile time constant. The simplest example of this is an array size:

#include <iostream>

using namespace std;

constexpr auto square(int x) { return x * x; }

int main()
{
    int arr[square(2)] = { 0, 1, 2, 3 };
    cout << arr[square(1)] << endl;
}

Without the constexpr on function square() it could not be called in the definition of arr since an array size is required to be a compile time constant.

While you could write a template that would give you a compile time square prior to constexpr, you could not use that template with a non compile time constant argument so you would end up with code duplication for compile time and non compile time versions. The syntax for a template is more complicated and less familiar to most programmers than a simple function definition as well.

In fact constexpr makes very few guarantees about when a compiler will actually choose to evaluate something at compile time. In contexts where a value is required to be a compile time constant (such as an array size) it will be of course. In other contexts it's largely up to the compiler - in the call to square() when accessing arr[square(1)] for example the compiler is free to evaluate at runtime, although in practice I would expect most compilers to evaluate this at compile time, at least in an optimized build.

mattnewport
  • 13,728
  • 2
  • 35
  • 39
  • Could you give an example of how you would do this using a template? –  Aug 27 '15 at 11:32
  • 1
    @AaryamanSagar http://coliru.stacked-crooked.com/a/e8a6dd15a3540b1e gives an example using both a `constexpr` function and a template. – mattnewport Aug 27 '15 at 14:40
  • thanks! but the part of my question that confuses me is which static variables are resolved at compile time and which are not. But I guess the compiler simply picks those which have been inlined and assigns them their value at compile time –  Aug 27 '15 at 16:19
  • @AaryamanSagar there aren't many guarantees what is resolved at compile time. Basically, if an expression is used in a context that requires a compile time constant (e.g. template arguments, array sizes, `alignas` arguments, enum initializers and a few other cases) then it is evaluated at compile time. In any other situation that doesn't require a compile time constant it is pretty much up to the compiler whether the expression is evaluated at compile time or not and this may depend on optimization settings etc. – mattnewport Aug 27 '15 at 17:13
  • Yep that's what I thought. Thanks for answering! –  Aug 27 '15 at 17:14
7

This is not a great question because constexpr is a huge feature and it has many "points" to it.

The main one is, you can do compile-time computation using (more) normal C++ syntax than the template metaprogramming language.

For instance if you ever tried to do string manipulations at compile time using templates, you might contrast that experience with some of the techniques described here: Conveniently Declaring Compile-Time Strings in C++

constexpr is basically a new compile-time metaprogramming language which exists in parallel to the template language, and some things like constexpr constructors allow you to instantiate structs at compile time which simply isn't possible with only templates.

Another big "point" of it is that you don't have to write different code for compile-time vs. run-time. Code that is marked constexpr can run at compile-time and can also run just as easily at run-time. Template code... has to run entirely at compile time and generally looks very different from equivalent run-time code. So in some cases using constexpr code is more DRY.

Community
  • 1
  • 1
Chris Beck
  • 15,614
  • 4
  • 51
  • 87
  • That didn't really answer the question I had in mind. I edited my question, so hopefully it will now be more clear to others.. –  Aug 27 '15 at 04:43
  • Ok, I think you have multiple questions here now. But the "what then is the point" is answered pretty well by my answer. Sure you get a compile time bool with template, but not a compile time struct. And the template code is sometimes less DRY. The "How can I tell what is actually compile time and what is not" is really a different question. – Chris Beck Aug 27 '15 at 04:47
  • Thanks for your answer! –  Aug 27 '15 at 04:50
  • 3
    Upped. I just think the first line should read "this `is` a great question", for that very reason. – Victor Sergienko Dec 16 '16 at 19:54