Can C++ constexpr function actually accept non-constant expression as argument? - c++

I have defined a constexpr function as following:
constexpr int foo(int i)
{
return i*2;
}
And this is what in the main function:
int main()
{
int i = 2;
cout << foo(i) << endl;
int arr[foo(i)];
for (int j = 0; j < foo(i); j++)
arr[j] = j;
for (int j = 0; j < foo(i); j++)
cout << arr[j] << " ";
cout << endl;
return 0;
}
The program was compiled under OS X 10.8 with command clang++. I was surprised that the compiler did not produce any error message about foo(i) not being a constant expression, and the compiled program actually worked fine. Why?

The definition of constexpr functions in C++ is such that the function is guaranteed to be able to produce a constant expression when called such that only constant expressions are used in the evaluation. Whether the evaluation happens during compile-time or at run-time if the result isn't use in a constexpr isn't specified, though (see also this answer). When passing non-constant expressions to a constexpr you may not get a constant expression.
Your above code should, however, not compile because i is not a constant expression which is clearly used by foo() to produce a result and it is then used as an array dimension. It seems clang implements C-style variable length arrays as it produces the following warning for me:
warning: variable length arrays are a C99 feature [-Wvla-extension]
A better test to see if something is, indeed, a constant expression is to use it to initialize the value of a constexpr, e.g.:
constexpr int j = foo(i);

I used the code at the top (with "using namespace std;" added in) and had no errors when compiling using "g++ -std=c++11 code.cc" (see below for a references that qualifies this code) Here is the code and output:
#include <iostream>
using namespace std;
constexpr int foo(int i)
{
return i*2;
}
int main()
{
int i = 2;
cout << foo(i) << endl;
int arr[foo(i)];
for (int j = 0; j < foo(i); j++)
arr[j] = j;
for (int j = 0; j < foo(i); j++)
cout << arr[j] << " ";
cout << endl;
return 0;
}
output:
4
0 1 2 3
Now consider reference https://msdn.microsoft.com/en-us/library/dn956974.aspx It states: "...A constexpr function is one whose return value can be computed at compile when consuming code requires it. A constexpr function must accept and return only literal types. When its arguments are constexpr values, and consuming code requires the return value at compile time, for example to initialize a constexpr variable or provide a non-type template argument, it produces a compile-time constant. When called with non-constexpr arguments, or when its value is not required at compile-time, it produces a value at run time like a regular function. (This dual behavior saves you from having to write constexpr and non-constexpr versions of the same function.)"
It gives as valid example:
constexpr float exp(float x, int n)
{
return n == 0 ? 1 :
n % 2 == 0 ? exp(x * x, n / 2) :
exp(x * x, (n - 1) / 2) * x;
}

This is an old question, but it's the first result on a google search for the VS error message "constexpr function return is non-constant". And while it doesn't help my situation, I thought I'd put my two cents in...
While Dietmar gives a good explanation of constexpr, and although the error should be caught straight away (as it is with the -pedantic flag) - this code looks like its suffering from some compiler optimization.
The value i is being set to 2, and for the duration of the program i never changes. The compiler probably noticed this and optimized the variable to be a constant (just replacing all references to variable i to the constant 2... before applying that parameter to the function), thus creating a constexpr call to foo().
I bet if you looked at the disassembly you'd see that calls to foo(i) were replaced with the constant value 4 - since that is the only possible return value for a call to this function during execution of the program.
Using the -pedantic flag forces the compiler to analyze the program from the strictest point of view (probably done before any optimizations) and thus catches the error.

Related

Declaring array with const and constexpr [duplicate]

This question already has an answer here:
Can an array be declared with a size that is a const variable not a constexpr?
(1 answer)
Closed 2 years ago.
While investigating the constexpr keyword in C++, I've come up with the following code:
#include <iostream>
int main() {
const int n = 10;
constexpr int n2 = 10;
int a1[n];
int a2[n2];
std::cout << "n " << n << std::endl;
std::cout << "n2 " << n2 << std::endl;
}
I would expect that declaring the array a1 with "const" would not work and the compiler would at least give me a warning (assuming the compilation is done with g++ -Wall -pedantic constexpr_1.cpp -o ce1) but it does not. I've seen some error with VS compiler so any hint is welcome here.
In C++, the size of each array dimension in an array declaration must be an integral constant expression.
A constant expression (with some exceptions) is a value that can be evaluated at compile-time. That includes const variables with automatic storage duration initialized by a constant expression [ref].
Therefore both const int n = 10; and constexpr int n2 = 10; are usable as a size in array declarations and the code example is valid.
Note: this is not the case in C - so make sure to compile in C++ mode [ref].
The code also compiles fine in MSVC 2015+. But it could very well be that an archaic VC++ compiler has a bug that prevents this from compiling.

How to understand constexpr in this example?

I'm trying to understand the meaning of constexpr when applied to functions. In the example below the program compiles and runs but I don't understand how the function sum(int n) can be deduced at compile time as n is not known until run time. I'm using VS 2017 with latest updates.
The program compiles whether constexpr is included or not.
#include <iostream>
constexpr int sum(int n)
{
return (n <= 0) ? 0 : n + sum(n-1);
}
int main()
{
int i;
std::cin >> i;
std::cout << sum(i) << std::endl;
return 0;
}
I expected the compiler to error that sum(int n) is not a constant expression. Or is constepxr just a hint to the compiler like "inline", that it is free to ignore?
I expected the compiler to error that sum(int n) is not a constant expression.
constexpr int sum(int n); means that the function can be evaluated at compile time. It doesn't have to be. You can call it at runtime without any issues, which makes sense to not force programmers to duplicate code when they need identical functionality at runtime as well as at compile time.
With C++20, you'll be able to trigger the error you were expecting by qualifying the function with the new keyword consteval instead of constexpr.
consteval int sum(int n)
{
// As before...
}
int i;
// determine i at runtime...
sum(i); // Error! Not evaluated at compile time.
You can have a look at P1073 for this feature. This proposal has been approved for the next standard.
The constexpr keyword says that the function must be evaluated at compile time, if it's called in a constexpr context.
Consider:
constexpr int sum(int n)
{
return (n <= 0) ? 0 : n + sum(n-1);
}
int main()
{
int i;
std::cin >> i;
constexpr int s1 = sum(4); // OK, evaluated at compile time
int s2 = sum(i); // OK, evaluated at run time
constexpr int s3 = sum(i); // Error, i cannot be evaluated at compile time
int s4 = sum(4); // OK, execution time depends on the compiler's mood
}
Here, s3 is constexpr and so it's initializer needs to be evaluated at compile time. Hence the error.
If this feature weren't there, you would have to write two versions of your function, one for compile time use and the other for run-time use.
See it yourself on the Compiler Explorer.
Also note that constexpr implies inline for functions.
Here is what my perspective is
constexpr, ensures that the constant must be a compile-time constant
Thus
constexpr double pi (3.5); // is Okay because resolution is at compile time
and this
// Should be Okay be cause the resolution of **sum** is at compiletime
// evaluation however is run-time dependent
constexpr int sum(int n)
{
return (n <= 0) ? 0 : n + sum(n-1);
}
Another example is something like this
constexpr int compute(int x) {
return x+1;
}
int foo[compute(15)];

constexpr function and its parameter

constexpr int hello(int j) {
return j * 12;
}
constexpr int bye(int j = 6) {
return j * 12;
}
int main() {
int i = 6;
constexpr int a1 = hello(i); //error
constexpr int a2 = hello(6); //ok
constexpr int a3 = hello(int(6)); //ok
constexpr int a4 = bye(); //ok
constexpr int a5 = bye(i); //error
constexpr int a6 = bye(6); //ok
return 0;
}
What's difference between hellow(i) and hello(6)? I think one is int j = i; and j is not a constexpr, while the other is int j = 6 and j is still not a constexpr, both j are int type.
int * literal != constexpr, so the return type is not a constexpr.
I got the above conclusion from an example in the book:
int staff_size = 27; //staff_size is not a const expression
Although staff_size is initialized from a literal, it is not a constant expression
because it is a plain int, not a const int.
Moreover, I notice that hello(int(6)) works fine too, what's the "thing" here?
Additionally, bye() works while hello(i) does not, both parameter are initialized inside the function, only one with default value, what's the point here?
Very confused, hope someone could explain :D
PS: have no idea to figure out a better title, sorry for that
The difference between hello(6) and hello(i) is that the 6 in hello(6) is a constexpr function parameter, while the i in the hello(i) is a regular int parameter.
If you declare i to be constexpr int i = 6;, then hello(i) will compile and execute.
In hello(int(6)), you're saying cast 6, an integer literal, into an int. This is a redundant operation. hello(int(6)) and hello(6) will have similar, if not identical behavior.
bye() works since the function parameter uses the default parameter (the j = 6 in constexpr int bye(int j = 6);). The default initialized parameter is known at compile time, thus, is a constexpr by definition
The thing to note here is that the compiler "validates" the code, it doesn't "read" it.
The compiler expects all parts of a constexpr to be a known valid constant at compile time. So although you and I know, from reading the code, that the value of i never changes, the compiler doesn't "know" it unless you declare i to be a constant. As far as it is concerned you could have executed other code somewhere to change the value of i.
In all the cases where the function is called without the i, the compiler knows for a fact, without a doubt, that the value of j is a constant integer with a value of 6, (note: int(6) is the same as just 6).

'Constant Expression Required' Error while keeping formal argument as a constant

This is a C++ programming code to display the values of array1 and array2 but I am getting a compile time error as 'Constant Expression Required'. Please Help
void display(const int const1 = 5)
{
const int const2 = 5;
int array1[const1];
int array2[const2];
for(int i = 1 ; i < 5 ; i++)
{
array1[i] = i;
array2[i] = i * 10;
std::cout << array1[i] << std::endl;
}
}
void main()
{
display(5);
}
In C++, const is not always constexpr. Back in the days, constexpr didn't exist, so the only way of having a compile time constant was to either use const with a literal, or to use enum, because both of these are easy for the compiler to check the value.
However, in C++11, we added constexpr, which guaranties that a constexpr variable has a value available at compile-time, and state that constexpr function can be evaluated aat compile time if all arguments are constexpr too.
In your code, you can write your variable const2 like this:
void display(const int const1=5)
{
constexpr int const2 = 5;
// ...
}
Now your code is much more expressive about what you are doing. instead of relying that the const may be available at compile time, you say "this variable has a value known at compile time, here's the value".
However, if you try to change const1, you'll get an error. Parameters, even with default value always as a value known at runtime. If the value is only known at runtime, you can't use it in template parameters or array size.
If you want your function to be able to receive the value const1 as a constant expression from where you can receive it as a template parameter, since template parameters are always known at compile time.
template<int const1 = 5>
void display()
{
constexpr int const2 = 5;
int array1[const1];
int array2[const2];
}
You will have to call your function like that:
// const1 is 5
display();
// const1 is 10
display<10>();
If you want to know more about templates, go check Function templates, or this tutorial

C++ Expression evaluation. What does 'evaluation' mean?

I understand the problems with the classic example of
int i=0;
foo(i++, i++);
but I can't convince myself of whether the following is valid or invalid
int foo(int& i)
{
i=42;
return 99;
}
bar(foo(i), i);
I understand that the order 'foo(i)' and 'i' are evaluated is undefined, but what exactly does 'evaluated' mean? i.e. will the 2nd parameter of bar always be 42, or can the current value of 'i' be passed in before foo changes it?
No it is not guaranteed.
The order of evaluation of arguments to an function is Unspecified[Ref 1].
It can be possible that either:
foo(i) gets evaluated first or
i gets evaluated or
any other magical order(in case number of arguments were more than two)
Unspecified in this context means the implementation is allowed to implement the said feature whichever way they want and it need not be documented.
[Ref 1]
C++03 5.2.2 Function call
Para 8
The order of evaluation of arguments is unspecified. All side effects of argument expression evaluations take effect before the function is entered. The order of evaluation of the postfix expression and the argument expression list is unspecified.
This sample (gcc 4.6)
#include <iostream>
using namespace std;
int foo(int& i)
{
i=42;
return 99;
}
void bar(int i, int j)
{
cout << "i = " << i << "; j = " << j << endl;
}
int main()
{
int i =10;
bar(foo(i), i);
return 0;
}
gives i = 99, j = 10.
So it is really not guaranteed.