Why is this not a constant expression?
Solution 1
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
.
Solution 2
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).
Solution 3
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.
Solution 4
Because arr[i]
is not compile-time constant expression. It can be different at run-time.
Chris_F
Updated on October 21, 2021Comments
-
Chris_F over 2 years
In this trivial example,
test2
fails to compile even thoughtest1
succeeds, and I don't see why that is the case. Ifarr[i]
is suitable for a return value from a function markedconstexpr
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 }