Why is this not a constant expression?

c++, c++11, c++14, constexpr, templates

Solution

Short answer: there are no `constexpr` function parameters in `C++11/14`.

Longer answer: in `test1()`, if `i` is not a compile-time constant, the function is still usable at run-time. But in `test2()`, it cannot be known to the compiler whether `i` is a compile-time constant, and yet it is required for the function to compile.

E.g. the following code for `test1` will compile

int i = 0;    
char a = test1("Test", i); // OK, runtime invocation of test1()

constexpr int i = 0;
constexpr char a = test1("Test", i); // also OK, compile time invocation of test1()

Let's simply your `test2()` to

constexpr char test3(unsigned i)
{
    return t<i>::value;
}

This will not compile for `test3(0)` because inside `test3()`, it cannot be proven that `i` is an unconditional compile-time expression. You would need `constexpr` function parameters to be able to express that.

Quote from the Standard

5.19 Constant expressions [expr.const]

2 A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:

— an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either — it is initialized with a constant expression or

— it is a non-static data member of an object whose lifetime began within the evaluation of e;

This section has the following code example corresponding to your question:

constexpr int f1(int k) {
    constexpr int x = k; // error: x is not initialized by a
                         // constant expression because lifetime of k
                         // began outside the initializer of x
    return x;
}

Because `x` in the above example is not a constant expression, it means that you can't instantiate templates with either `x` or `k` inside `f1`.

Problem

In this trivial example, `test2` fails to compile even though `test1` succeeds, and I don't see why that is the case. If `arr[i]` is suitable for a return value from a function marked `constexpr` then why can it not be used as a non-type template argument? ``` template<char c> struct t { static const char value = c; }; template <unsigned N> constexpr char test1(const char (&arr)[N], unsigned i) { return arr[i]; } template <unsigned N> constexpr char test2(const char (&arr)[N], unsigned i) { return t<arr[i]>::value; } int main() { char a = test1("Test", 0); //Compiles OK char b = test2("Test", 0); //error: non-type template argument //is not a constant expression } ``` Edit: This makes no difference: ``` template<char c> struct t { static const char value = c; }; template <unsigned N> constexpr char test1(const char (&arr)[N]) { return arr[0]; } template <unsigned N> constexpr char test2(const char (&arr)[N]) { return t<arr[0]>::value; } int main() { char a = test1("Test"); //Compiles OK char b = test2("Test"); //error: non-type template argument //is not a constant expression } ```

Original source