conditional evaluation of functions - c++

Ok, this might be a stupid question, but I completely don't understand chapter 12.1.6.2 - conditional evaluation under constexpr functions of the c++ programming language. This is the whole very short text.
A branch of a conditional expression that is not taken in a constexpr
function is not evaluated. This implies that a branch not taken can
require run-time evaluation. For example:
constexpr int check(int i)
{
return (low<=i && i<high) ? i : throw out_of_range();
}
constexpr int low = 0;
constexpr int high = 99;
// ...
constexpr int val = check(f(x,y,z));
You might imagine low and high to be configuration parameters that are
known at compile time, but not at design time, and that f(x,y,z)
computes some implementation-dependent value.
Source for context
I tried running the code above to try to more understand the explanation but I'm getting an error. Can someone provide a clearer explanation?
Edit: I created a program to test this:
#include<iostream>
#include<stdexcept>
using namespace std;
constexpr int low = 0;
constexpr int high = 99;
constexpr int check(int i) {
return (low<=i && i<high) ? i : throw out_of_range();
}
constexpr int f(int x, int y, int z) {
return x*y*z;
}
int main() {
constexpr int val = check(f(2,2,2));
cout << val << '\n';
}
It won't run:
no matching function for call to 'std::out_of_range::out_of_range()' //I'm really surprised at this
return (low<=i && i<high) ? i : throw out_of_range();
error: body of constexpr function 'constexpr int check(int)' not a return-statement
}
error: 'constexpr int check(int)' called in a constant expression
constexpr int val = check(f(2,2,2));

It means that a conditional branch in a constexpr function is allowed to use non-constant expressions (i.e. ones requiring run-time evaluation, such as throwing exceptions) as long as that branch is never taken when the function is called in a constant expression context.
So it's OK to call check to initialized the constexpr variable val, as long as the arguments to the function are constant expressions, and the condition (low<=i && i<high) is true.
If the arguments are not constants, the function call is not a constant expression and so can't initialize a constexpr variable.
If the condition is false the function needs to take the false branch, which needs to throw an exception, which requires run-time evaluation, so the function is not a constant expression, and so cannot initialize a constexpr variable.
When the argument is a constant expression, the compiler knows at compile-time whether the branch will be taken, so as long as the condition is true it can completely ignore the false branch, and doesn't complain that throwing exceptions is impossible at compile-time.
When the function is called at run-time, it can have any arguments, and the false branch can be taken and the throw will be evaluated as for a normal (non-constexpr) function.

Related

constexpr, or not constexpr, that is the question

I was playing around with constexpr in C++ and noticed a strange behavior that I wish to understand. Consider this code from 5.19 section of Standard.
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;
}
As error states, lifetime of k began outside the initializer of x, thus we can't be sure that x will be a constant expression.
And here the another version of the same function.
constexpr int f1(int k) {
return k;
}
This one is totally fine and usable. So question is why, here also, the lifetime of k began outside of initializer of return value, or not, or this is because of RVO and technically if just follow the Standard this also should be an error?
And also another question, from which this one actually arise. I was trying to write constexpr IPv4 class. For that purpose I've used constructor with std::string_view. So I was able to have this in compile time using gcc 10.3 and -std=c++20
constexpr IPv4 myIP{"192.168.0.0"};
constexpr size_t count = myIP.countOfDots(); // calls std::count which is constexpr in C++20
Now I want to validate that IP is correct, so I need to check count of dots to be equal to 3, which I can easily do here by
if constexpr (count != 3)
The question is how to organize this into some function, which will also allow me to do such a check in compile time for any given IP, basically I want something like this
constexpr bool validateIP(const IPv4& ip);
And as in the example above, I can't just have this in that function
constexpr size_t count = ip.countOfDots();
So is it possible to do the way I want?
A function that's constexpr means that the function potentially can be evaluated at compile-time. The function must however also be callable with a run-time value, and produce a run-time result.
A variable that's constexpr has to be a compile time constant. Period.
What that means for your validateIP function is that you don't need to make count constexpr. You can write a function and mark it as constexpr.
When you call the function with a compile time constant, it will get evaluated at compile time.
If you call it with a run time value it will get evaluated at run time.
#include <string_view>
#include <iostream>
constexpr bool validateIP(const std::string_view& ip) {
int count = 0;
for (auto& c : ip) {
if (c == '.') {
++count;
}
}
return count == 3;
}
int main()
{
// Assigning the value to a constexpr is not needed to make the function
// be evaluated at compile time, but it proves that it is in this case
constexpr auto isValid1 = validateIP("123.456.789.0");
std::cout << isValid1;
}

constexpr - What does "Evaluate value at compile time" mean exactly?

#include <array>
int value1(int param) {
return param * 2;
}
constexpr int value2(int param) {
return param * 2;
}
int main() {
const int i = 10;
std::array<int, value1(i)> starr1 = {}; // 1
std::array<int, value2(i)> starr2 = {}; // 2
return 0;
}
2 is okay, but 1 gives a compile error because std::array has to make static size array. value2() returns compile-time constant value because of constexpr keyword.
So, how does the compiler infer that value2(i) is compile-time constant? Does it call the function value2() while compiling?
const int value1(int param) {
return param * 2;
}
int main() {
const int i = 10;
std::array<int, value1(i)> starr1 = {}; // 3
return 0;
}
>>> error: call to non-constexpr function ‘const int value1(int)’
Also, 3 still tgives a compile error. Is value1(i) not compile-time constant even though const keyword is applied to the function value1()?
So, how compiler infer value2(i) is compile-time constant?
It doesn't infer that. You state that explicitly when you annotate it with constexpr. It might infer that for functions not marked with constexpr, though. This still won't allow you to use their results in compile-time expressions, and is only used as an optimization strategy.
Does it call the function value2() while compiling?
In a sense, yes. It's probably closer to interpreting it directly, since I don't think any compiler actually compiles that function for the purposes of executing it during the build. What matters is that it's able to establish its result before the entire program is built and ran, and that it can use that result to e.g. determine the size of your array when generating the code.
Is value1(i) not compile constant even though const keyword is applied to the function value1()?
It's not. const only applies to the return type (and in this case, it's effectively useless), not the evaluation possibility in compile-time.

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)];

Comparing constexpr function parameter in constexpr-if condition causes error

I'm trying to compare a function parameter inside a constexpr-if statement.
Here is a simple example:
constexpr bool test_int(const int i) {
if constexpr(i == 5) { return true; }
else { return false; }
}
However, when I compile this with GCC 7 with the following flags:
g++-7 -std=c++1z test.cpp -o test
I get the following error message:
test.cpp: In function 'constexpr bool test_int(int)':
test.cpp:3:21: error: 'i' is not a constant expression
if constexpr(i == 5) { return true; }
However, if I replace test_int with a different function:
constexpr bool test_int_no_if(const int i) { return (i == 5); }
Then the following code compiles with no errors:
int main() {
constexpr int i = 5;
static_assert(test_int_no_if(i));
return 0;
}
I don't understand why the constexpr-if version fails to compile, especially since the static_assert works just fine.
Any advice on this would be appreciated.
Thanks!
From constexpr if:
In a constexpr if statement, the value of condition must be a
contextually converted constant expression of type bool.
Then, from constant expression:
Defines an expression that can be evaluated at compile time.
Obviously, i == 5 is not a constant expression, because i is a function parameter which is evaluated at run time. That is why the compiler complains.
When you use a function:
constexpr bool test_int_no_if(const int i) { return (i == 5); }
then it might be evaluated during the compile time depending on whether it's parameter is known at compile time or not.
If i is defined like:
constexpr int i = 5;
then the value of i is known during the compile time and test_int_no_if might be evaluated during the compile too making it possible to call it inside static_assert.
Also note, that marking function parameter as const does not make it a compile time constant. It just means that you cannot change the parameter inside the function.
A constexpr function can be called with non-constexpr arguments, in which case it behaves like a normal function, so the code must still compile as if it were not constexpr.
In short, there's nothing in test_int_no_if that depends on i being constexpr, while in test_int(), there is. ("constexpr if" only works with compile time expressions.)

Difference between "if constexpr()" Vs "if()"

What is the difference between if constexpr() and if()?
Where and When can I use both of them?
The only difference is that if constexpr is evaluated at compile time, whereas if is not. This means that branches can be rejected at compile time, and thus will never get compiled.
Imagine you have a function, length, that returns the length of a number, or the length of a type that has a .length() function. You can't do it in one function, the compiler will complain:
template<typename T>
auto length(const T& value) noexcept {
if (std::integral<T>::value) { // is number
return value;
else
return value.length();
}
int main() noexcept {
int a = 5;
std::string b = "foo";
std::cout << length(a) << ' ' << length(b) << '\n'; // doesn't compile
}
Error message:
main.cpp: In instantiation of 'auto length(const T&) [with T = int]':
main.cpp:16:26: required from here
main.cpp:9:16: error: request for member 'length' in 'val', which is of non-class type 'const int'
return val.length();
~~~~^~~~~~
That's because when the compiler instantiates length, the function will look like this:
auto length(const int& value) noexcept {
if (std::is_integral<int>::value) { // is number
return value;
else
return value.length();
}
value is an int, and as such doesn't have a length member function, and so the compiler complains. The compiler can't see that statement will never be reached for an int, but it doesn't matter, as the compiler can't guarantee that.
Now you can either specialize length, but for a lot of types (like in this case - every number and class with a length member function), this results in a lot of duplicated code. SFINAE is also a solution, but it requires multiple function definitions, which makes the code a lot longer than it needs to be compared to the below.
Using if constexpr instead of if means that the branch (std::is_integral<T>::value) will get evaluated at compile time, and if it is true then every other branch (else if and else) gets discarded. If it is false, the next branch is checked (here else), and if it is true, discard every other branch, and so on...
template<typename T>
auto length(const T& value) noexcept {
if constexpr (std::integral<T>::value) { // is number
return value;
else
return value.length();
}
Now, when the compiler will instantiate length, it will look like this:
int length(const int& value) noexcept {
//if (std::is_integral<int>::value) { this branch is taken
return value;
//else discarded
// return value.length(); discarded
}
std::size_t length(const std::string& value) noexcept {
//if (std::is_integral<int>::value) { discarded
// return value; discarded
//else this branch is taken
return value.length();
}
And so those 2 overloads are valid, and the code will compile successfully.
The ordinary if statement:
Has its condition evaluated every time control reaches it, if ever
Determines which of the two substatements to execute, skipping the other
Requires both substatements to be well-formed regardless of which one is actually selected at runtime
The if constexpr statement:
Has its condition evaluated at compile time once all necessary template arguments have been supplied
Determines which of the two substatements to compile, discarding the other
Does not require the discarded substatement to be well-formed