Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this not a constant expression?

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
}
like image 544
Chris_F Avatar asked Jul 04 '14 21:07

Chris_F


People also ask

What is a constant expression?

A constant expression is an expression that contains only constants. A constant expression can be evaluated during compilation rather than at run time, and can be used in any place that a constant can occur.

What is the constant expression in C++?

A constant value is one that doesn't change. C++ provides two keywords to enable you to express the intent that an object is not intended to be modified, and to enforce that intent. C++ requires constant expressions — expressions that evaluate to a constant — for declarations of: Array bounds.

What is Constexpr in C ++ 11?

The keyword constexpr was introduced in C++11 and improved in C++14. It means constant expression. Like const , it can be applied to variables: A compiler error is raised when any code attempts to modify the value. Unlike const , constexpr can also be applied to functions and class constructors.


3 Answers

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.

like image 114
TemplateRex Avatar answered Sep 24 '22 00:09

TemplateRex


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).

like image 21
Useless Avatar answered Sep 20 '22 00:09

Useless


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.

like image 27
101010 Avatar answered Sep 21 '22 00:09

101010