constexpr function and its parameter - c++

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

Related

C++ pointer of array: why cannot use an array defined by indirectly computed const var in function begin(array)?

Error message in text:
I'm studying the book C++ Primer and encountering a problem listed below when coding an answer for one exercise:
#include<iostream>
#include<vector>
using namespace std;
int main() {
int i = 3;
const int ci = 3;
size_t si = 3;
const size_t csi = 3;
int ia[i];
int cia[ci];
int sia[si];
int csia[csi];
int another_a[] = {1,2,3};
int *pi = begin(ia); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [i])
int *pci = begin(cia);
int *psi = begin(sia); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [si])
int *pcsi = begin(csia);
int *p_ano = begin(another_a);
vector<int> v = {1,3,4};
const int m = v.size();
const size_t n = v.size();
int ma[m];
int na[n];
int *pm = begin(ma); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [m])
int *pn = begin(na); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [n])
system("pause");
return 0;
}
I can understand that the first two errors are because that those two arrays are not defined using an constant variable.
But why the last two, even if I have converted the size of the vector into a constant variable, the compiler still reports an error?
I'm quite confused about this, I would appreciate a lot for your kindly answer or discussion no matter it works or not.
First and foremost, you are using a compiler extension, but more on that later.
The standard begin overload which works for you is a template that accepts a reference to an array with a size that is a constant expression. In a nutshell, constant expressions are those expressions that a compiler can evaluate and know the value of during compilation.
A constant integer initialized with a constant expression like const int ci = 3;, can be used wherever a constant expression is required. So ci is, for all intents an purposes, a constant expression itself (equal to 3).
Modern C++ has a way to make such varaibles stand out as intended constant expressions, it's the constexpr specifier. So you could define ci like this:
constexpr int ci = 3;
It's exactly like your original code. But the same will not work for const int m = v.size();. Because constexpr requires a true constant expression as an initializer, unlike const. For a const variable is not necessarily a constant expression. It can just be a run-time variable that you cannot modify. And this is the case with m.
Because m is not a constant expression, what you defined is a variable length array. A C feature that is sometimes introduced as an extension by C++ compilers. And it doesn't gel with the std::begin template, which expects the array extent to be a constant expression.
Declaring arrays with non constant indexes isn't standard c++.
If you need dynamically sized arrays use std::vector.
Declaring a variable as const doesn't make it a compile time constant (required to declare a fixed sized array) it just means you can't modify it after it is declared.

'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

Getting 'Non-constant expression as array bound' when field is const

I'm trying to define a multidimensional array using my constant field as its dimension, but I'm getting a compilation error saying that the expression is not constant. Is there any other way to do this so I can use a constant field defined in constructor initialization list as an array dimension?
Translation for English-speaking majority:
class FunctionWave2D : public DisallowedDomainPoints
{
protected:
double th;
double l; a
double d, dd;
const int number_sqrt; //here's the constant
double **second_derivatives;
protected:
bool elasticTenstionOnly;
public:
FunctionWave2D(int number, double elasticModulus, double dampingFactor, double oscillationDampingFactor, double length)
:DisallowedDomainPoints(number * LAYER_COUNT),
th(elasticModulus), d(dampingFactor), dd(oscillationDampingFactor),
elasticTensionOnly(false),
l(length/(sqrt(number)-1)),
number_sqrt(sqrt(number))
{
second_derivatives = new double[number_sqrt][number_sqrt][LAYER_COUNT];
//(...)
In C++, the term "constant expression" specifically refers to an expression whose value is known at compile-time. It's not the same as a const variable. For example, 137 is a constant expression, but in this code:
int function(int x) {
const int k = x;
}
The value of k is not a constant expression, since its value can't be determined at compile-time.
In your case, you have a data member declared as
const int ilosc_sqrt; //here's the constant
Even though this is marked const, its value is not known at compile-time. It is initialized in the initializer list as
ilosc_sqrt(sqrt(ilosc))
This value can't be determined until the program is actually run, hence the error. (Note that the new C++11 constexpr keyword is designed, among other things, to make constant expressions a lot easier to identify in source code and to make it possible to do more advance compile-time computations with constants.)
To fix this, you will either need to split up your initialization into smaller steps:
drugie_pochodne = new double**[ilosc_sqrt];
for (int i = 0; i < ilosc_sqrt; i++) {
drugie_pochodne[i] = new double*[ilosc_sqrt];
for (int j = 0; j < ilosc_sqrt; j++) {
drugie_pochodne[j] = new double[ILOSC_WARSTW];
}
}
Or use a library like Boost.MultiArray, which supports a cleaner initialization syntax.
Hope this helps!
An array bound has to be a compile-time constant. A non-static const data member is not a compile-time constant; it gets its value at runtime, when the object is constructed.
So, basically, if you need to set the size of that array at runtime you'll have to build up all the pieces with operator new[]. Essentially,
int **data_2d = new int*[runtime_size];
for (int i = 0; i < runtime_size; ++i)
data_2d[i] = new int[runtime_size];
The extension to a 3d-array is straightforward.

const vs constexpr on variables

Is there a difference between the following definitions?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
If not, which style is preferred in C++11?
I believe there is a difference. Let's rename them so that we can talk about them more easily:
const double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;
Both PI1 and PI2 are constant, meaning you can not modify them. However only PI2 is a compile-time constant. It shall be initialized at compile time. PI1 may be initialized at compile time or run time. Furthermore, only PI2 can be used in a context that requires a compile-time constant. For example:
constexpr double PI3 = PI1; // error
but:
constexpr double PI3 = PI2; // ok
and:
static_assert(PI1 == 3.141592653589793, ""); // error
but:
static_assert(PI2 == 3.141592653589793, ""); // ok
As to which you should use? Use whichever meets your needs. Do you want to ensure that you have a compile time constant that can be used in contexts where a compile-time constant is required? Do you want to be able to initialize it with a computation done at run time? Etc.
No difference here, but it matters when you have a type that has a constructor.
struct S {
constexpr S(int);
};
const S s0(0);
constexpr S s1(1);
s0 is a constant, but it does not promise to be initialized at compile-time. s1 is marked constexpr, so it is a constant and, because S's constructor is also marked constexpr, it will be initialized at compile-time.
Mostly this matters when initialization at runtime would be time-consuming and you want to push that work off onto the compiler, where it's also time-consuming, but doesn't slow down execution time of the compiled program
constexpr indicates a value that's constant and known during compilation.
const indicates a value that's only constant; it's not compulsory to know during compilation.
int sz;
constexpr auto arraySize1 = sz; // error! sz's value unknown at compilation
std::array<int, sz> data1; // error! same problem
constexpr auto arraySize2 = 10; // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr
Note that const doesn’t offer the same guarantee as constexpr, because const
objects need not be initialized with values known during compilation.
int sz;
const auto arraySize = sz; // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value unknown at compilation
All constexpr objects are const, but not all const objects are constexpr.
If you want compilers to guarantee that a variable has a value that can be
used in contexts requiring compile-time constants, the tool to reach for is constexpr, not const.
A constexpr symbolic constant must be given a value that is known at compile time.
For example:
constexpr int max = 100;
void use(int n)
{
constexpr int c1 = max+7; // OK: c1 is 107
constexpr int c2 = n+7; // Error: we don’t know the value of c2
// ...
}
To handle cases where the value of a “variable” that is initialized with a value that is not known at compile time but never changes after initialization,
C++ offers a second form of constant (a const).
For Example:
constexpr int max = 100;
void use(int n)
{
constexpr int c1 = max+7; // OK: c1 is 107
const int c2 = n+7; // OK, but don’t try to change the value of c2
// ...
c2 = 7; // error: c2 is a const
}
Such “const variables” are very common for two reasons:
C++98 did not have constexpr, so people used const.
List item “Variables” that are not constant expressions (their value is not known at compile time) but do not change values after
initialization are in themselves widely useful.
Reference : "Programming: Principles and Practice Using C++" by Stroustrup
One more example to understand the difference between const and constexp.
int main()
{
int n;
cin >> n;
const int c = n; // OK: 'c' can also be initialized at run time
constexpr int e = n; // Error: 'e' must be initialized at compile time
}
Note: constexpr normally evaluated at compile-time, but they are not guaranteed to do so unless they're invoked
in a context where a constant expression is required.
constexpr int add(int a, int b)
{
return a + b;
};
int main()
{
int n = add(4, 3); // may or may not be computed at compile time
constexpr int m = add(4,3); // must be computed at compile time
}
constexpr -> Used for compile time constant. This is basically used for run time optimization.
const -> Used for run time constant.

How to convert int to const int to assign array size on stack?

I am trying to allocate a fixed size on stack to an integer array
#include<iostream>
using namespace std;
int main(){
int n1 = 10;
const int N = const_cast<const int&>(n1);
//const int N = 10;
cout<<" N="<<N<<endl;
int foo[N];
return 0;
}
However, this gives an error on the last line where I am using N to define a fixed
error C2057: expected constant expression.
However, if I define N as const int N = 10, the code compiles just fine.
How should I typecast n1 to trat it as a const int?
I tried : const int N = const_cast<const int>(n1) but that gives error.
EDIT : I am using MS VC++ 2008 to compile this... with g++ it compiles fine.
How should I typecast n1 to treat it as a const int?
You cannot, not for this purpose.
The size of the array must be what is called an Integral Constant Expression (ICE). The value must be computable at compile-time. A const int (or other const-qualified integer-type object) can be used in an Integral Constant Expression only if it is itself initialized with an Integral Constant Expression.
A non-const object (like n1) cannot appear anywhere in an Integral Constant Expression.
Have you considered using std::vector<int>?
[Note--The cast is entirely unnecessary. Both of the following are both exactly the same:
const int N = n1;
const int N = const_cast<const int&>(n1);
--End Note]
Only fixed-size arrays can be allocated that way. Either allocate memory dynamically (int* foo = new int[N];) and delete it when you're done, or (preferably) use std::vector<int> instead.
(Edit: GCC accepts that as an extension, but it's not part of the C++ standard.)