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
}
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.
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.
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.
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.
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
.
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With