27
votes

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
}
5

5 Answers

29
votes

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.

7
votes

There's a misconception of what constexpr does here. It indicates that a function must be evaluatable at compile time for suitable arguments, but it does not remove the requirement still to compile in the general case.

Let's take the first version:

template <unsigned N>
constexpr char test1(const char (&arr)[N], unsigned i) {
    return arr[i];
}

Now, this is clearly a compile-time evaluation:

enum { CompileTimeConstant = test1("Test", 0) };

your example may be, but it's an optimizer/QoI issue:

char MayBeCompileTimeConstant = test1("Test", 0);

and this example obviously isn't, but is still required to be evaluatable

char arr[10];
int i;
std::cin >> i;
std::cin >> arr;
char b = test1(arr, i);
std::cout << "'" << arr << "'[" << i << "] = " << b << '\n';

Since test2 can't possibly compile in for the last case, it can't compile at all. (Please note I'm not suggesting that code is good).

3
votes

The problem here is that calling arr[i] evokes the subscript operator operator[]. This operator doesn't return a constant expression.

It's not a problem of constexpr actually, is a problem of template argument deduction. A Non type template argument must be a constant expression which the return argument of subscript operator is not.

Therefore, the compiler rightfully complains that arr[i] is not a constant expression.

1
votes

Because arr[i] is not compile-time constant expression. It can be different at run-time.

0
votes

you can fix it with dynamic array sizing. you can use vector class for this, because in vector class you can resize your list dynamically. or you can use malloc but it would be such a waste of time while the vector class is standing there. alls im sayin