I am trying to pass a constant integer into a function using pass by reference.
#include <iostream>
using namespace std;
int test(int& num);
// changes a constant variable
int main() {
int loopSize = 7;
const int SIZE = loopSize;
cout<<SIZE<<endl;
test(loopSize);
cout<<SIZE;
return 0;
}
int test(int& num){
num -= 2;
}
However, the output is never updated.
SIZE and loopSize are two different objects. Even though SIZE started its life with loopSize's value at the time, changing one won't change the other. SIZE is not a reference.
Indeed, since SIZE is a constant you could never reasonably expect it to change anyway!
Is it possible that you intended to write the following?
const int& SIZE = loopSize;
// ^
You are changing loopSize and printing SIZE, so obviously the value won't change. Also, SIZE is a const, it won't change anyway.
Related
So I wrote this code ->
#include <iostream>
#include <bitset>
int main(){
int num, temp, digits = 0;
std::cin >> num;
temp = num;
while(temp){
temp /= 10;
++digits;
}
const int size = digits;
std::bitset<size> a(num);
std::cout << a << std::endl;
return 0;
}
The bitset container isnt accepting the const integer size as a parameter and throwing an error -Non-type template argument is not a constant expression. I want to know why this is happening as size has been declared as a constant and it's value isnt gonna change during the run-time of my program ?
A const variable can be interpreted differently, depending on what is assigned to it.
When assigned a compile time constant: it will be a compile time constant. That means that during compilation the constant value can be directly used in-place.
When assigned from another variable (which is not a compile time constant) : the new variable is unmodifilable. In that sense the variable is not a compile time constant. It cannot be modified in that block of code.
A template requires a compile time constant.
This question already has answers here:
How do I use arrays in C++?
(5 answers)
Closed 6 years ago.
I have looked at all the other posts with a similar topic, and none help, so please don't flag as a duplicate.
I am defining in main() a const int SIZE = 20;. Then, I pass this as an argument to my function, Mode:
int* Mode(int* numbers, int & mode, const int SIZE)
{
int occurences[SIZE];
// Calcualte mode
}
However, I get the error, expression must have a constant value.
My function call (in main) looks like this:
int* occurencesPtr = Mode(numbersPtr, mode, SIZE);
With SIZE being defined at the beginning to the literal 20.
I understand that the error is because the function's version of SIZE only acquires its value when the function is called (?), but I don't know how I could work around this.
I have even tried passing to the function a const int * const SIZEPtr = &SIZE, but that didn't work either. Help?
EDIT: I am not trying to use a variable size!! Notice that I have made SIZE a const everywhere! I just want to use that same SIZE constant to declare my array.
EDIT: Dynamic arrays are not what I need. I just want a normal, named, array, defined with a constant size value passed to the function.
There is a misconception here with what const means, probably because it's a little confusing that this works:
const int SIZE = 20;
int array[SIZE];
but this doesn't:
void foo(const int SIZE) {
int array[SIZE];
// ...
}
const int SIZE = 20;
foo(SIZE);
The issue is that the array size in an array declaration must be a core constant expression. Simplified, that means an expression that's evaluatable at compile time to be a constant. That is true in the first case (you can see that SIZE is the integral constant 20) but that is not true in the second case. There, the SIZE function parameter is just const - in the sense that it is nonmodifiable - and not a core constant expression. You can see the difference in that I can call foo() with something that is clearly unknowable until runtime:
int x;
if (std::cin >> x) {
foo(x);
}
In order to pass an argument into foo, and have that argument be used as an array bound, it is not enough to have it be const - the actual integral value must be encoded into the type (unless you call foo() as constexpr which I'm assuming is not the case here). In which case, you'd have to do something like:
template <int SIZE>
void foo() { ... }
const int SIZE = 20;
foo<SIZE>();
or:
template <int SIZE>
void foo(std::integral_constant<int, SIZE > ) { ... }
const int SIZE = 20;
foo(std::integral_constant<int, SIZE>{} );
or simply have SIZE be a global constant or otherwise accessible to foo() in a way that doesn't have to do with its arguments.
Or, there's always the simple option: use std::vector:
void foo(const int SIZE) {
std::vector<int> v(SIZE);
...
}
I understand that the error is because the function's version of SIZE only acquires its value when the function is called (?), but I don't know how I could work around this.
Option 1
Instead of defining SIZE in main, add a constexpr function. Use the constexpr function instead of passing the size.
constexpr int getSize()
{
return 20;
}
int* Mode(int* numbers, int & mode)
{
int occurences[getSize()];
// ...
}
Option 2
Use std::vector instead of array.
int* Mode(int* numbers, int & mode, int size)
{
std::vector<int> occurences[size];
// ...
}
Option 3
Use a function template.
template <size_t SIZE>
int* Mode(int* numbers, int & mode, int size)
{
int occurences[SIZE];
// ...
}
Option 4
Use a function template and std::array.
template <size_t SIZE>
int* Mode(int* numbers, int & mode, int size)
{
std::array<int, SIZE> occurences;
// ...
}
You're confusing things. A constant expression has nothing to do with const (at least not that much) ;).
let's think we are the compiler and face this function:
void foo(const int SIZE) { }
The constmerely says "we are not able to change the function-local variable SIZE inside the function body.
We need to compile it without assuming that SIZE is compile time constant. Why?
Because there is noone stoping us from doing something like:
int i{};
std::cin >> i;
foo(i);
You can pass any (matching/convertible) value to a by value const function argument.
What should happen when the compiler assumed the value passed to foo was a compile time constant expression?
If you want to pass compile time constants, use templates and while you're at it use std::array instead of T[N]:
template<std::size_t N>
void foo()
{
std::array<int, N> occurences;
}
const isn't doing what you think it's doing in your Mode function.
When const is used in function definition, const is simply telling the compiler that the function will not change the argument declared const inside of the scope of it's function. But that does not make the argument a constant, it is actually called a constant expression. Some compilers enforce this, others do not, and so will allow you to change const expressions (arguments passed with const keyword).
In order to use a globally accessible constant value which you can use, like SIZE, you'll need to declare a global constant before the function is called; which could be declared outside of main(), or at least outside the scope of all other functions but main(), if you must declare all inside main. Pass the global constant to the Mode function just as you would any other variable.
Oh, and, main() needs a return type.
I've edited the code to meet your specific constraints.
Here is a variation on your original code:
int main(){
//Declare constants first.
const int SIZE = 20; /*Could declare here instead.*/
//Declare variables next.
int *intPtr = 0; // to hold the pointer passed from Mode.
int *numbersPointer = 0;
int mode = 0;
//Define Mode (using OP's code.)
int* Mode(int* numbers, int & mode, const int size){
int occurences[size];
// Calculate mode
}
/*Now use constants, variables, and functions.*/
intPtr = Mode(numbersPointer, mode, SIZE); //Call mode.
return 0;
}
Say I have the following code:
#include <iostream>
using namespace std;
int defaultvalue[] = {1,2};
int fun(int * arg = defaultvalue)
{
arg[0] += 1;
return arg[0];
}
int main()
{
cout << fun() << endl;
cout << fun() << endl;
return 0;
}
and the result is:
2
3
which make sense because the pointer *arg manipulated the array defaultvalue. However, if I changed the code into:
#include <iostream>
using namespace std;
int defaultvalue[] = {1,2};
int fun(int arg[] = defaultvalue)
{
arg[0] += 1;
return arg[0];
}
int main()
{
cout << fun() << endl;
cout << fun() << endl;
return 0;
}
but the result is still:
2
3
Moreover, when I print out the defaultvalue:
cout << defaultvalue[0] <<endl;
It turn out to be 3.
My question is, in the second example, should the function parameter be passed by value, so that change of arg will have no effect on defaultvalue?
My question is, in the second example, should the function parameter be passed by value, so that change of arg will have no effect on defaultvalue?
No.
It is impossible to pass an array by value (thanks a lot, C!) so, as a "compromise" (read: design failure), int[] in a function parameter list actually means int*. So your two programs are identical. Even writing int[5] or int[24] or int[999] would actually mean int*. Ridiculous, isn't it?!
In C++ we prefer to use std::array for arrays: it's an array wrapper class, which has proper object semantics, including being copyable. You can pass those into a function by value just fine.
Indeed, std::array was primarily introduced for the very purpose of making these silly and surprising native array semantics obsolete.
When we declare a function like this
int func(int* arg);
or this
int (func(int arg[])
They're technically the same. It's a matter of expressiveness. In the first case, it's suggested by the API author that the function should receive a pointer to a single value; whereas in the second case, it suggests that it wants an array (of some unspecified length, possibly ending in nullptr, for instance).
You could've also written
int (func(int arg[3])
which would again be technically identical, only it would hint to the API user that they're supposed to pass in an int array of at least 3 elements. The compiler doesn't enforce any of these added modifiers in these cases.
If you wanted to copy the array into the function (in a non-hacked way), you would first create a copy of it in the calling code, and then pass that one onwards. Or, as a better alternative, use std::array (as suggested by #LightnessRacesinOrbit).
As others have explained, when you put
int arg[] as a function parameter, whatever is inside those brackets doesn't really matter (you could even do int arg[5234234] and it would still work] since it won't change the fact that it's still just a plain int * pointer.
If you really want to make sure a function takes an array[] , its best to pass it like
template<size_t size>
void func (const int (&in_arr)[size])
{
int modifyme_arr[100];
memcpy(modifyme_arr, in_arr, size);
//now you can work on your local copied array
}
int arr[100];
func(arr);
or if you want 100 elements exactly
void func (const int (&arr)[100])
{
}
func(arr);
These are the proper ways to pass a simple array, because it will give you the guaranty that what you are getting is an array, and not just a random int * pointer, which the function doesn't know the size of. Of course you can pass a "count" value, but what if you make a mistake and it's not the right one? then you get buffer overflow.
Is it possible to make a const& truely immutable?
int* side_effect;
void function(int const& i){
*side_effect = 123;
}
int main(){
int i = 0;
side_effect = &i;
//based on the function signature, i is suspected not to change.
//however, that is not the case.
function(i);
}
Are there any compiler warnings, attributes, compiler extensions, or language features I can employ to avoid these kinds of problems?
If you want a value to be a true constant, you can pass it as a template value argument.
template<int i>
void function(){
*side_effect = 123;
}
there is no way for any operation to modify i.
This requires that the input be a compile-time constant (and verified so by the compiler at compile time).
So this doesn't work:
int main(){
int i = 0;
side_effect = &i;
function<i>();
}
as i is not a compile-time constant. If we instead made it:
int main(){
const int i = 0;
side_effect = &i;
function<i>();
}
the function<i> line works, but the side_effect = &i doesn't work. If we add in a cast:
int main(){
const int i = 0;
side_effect = const_cast<int*>(&i);
function<i>();
}
now the operation *side_effect = 123 becomes UB within function.
Another approach which doesn't require that the value being passed in is a true compile-time constant is to not take a reference:
void function(int i){
*side_effect = 123;
}
and instead take a local copy (either int or const int as the argument, depending on if we want function to have the rights to modify i).
The full strength version of what you want -- that we take a reference to external data, and then ensure that the external data remains unchanged -- can pretty easily be shown to be equivalent to the halting problem in the general case.
#include <iostream>
using namespace std;
int main(void)
{
int num[5];
const int* &ref = num;
return 0;
}
I've read a C++ book which mentioned if a reference variable is referencing to:
a variable where the type is different but can be converted.
a variable that is not a Lvalue.
As long as the referencing variable is declare as const, the above 2 cases will be solved by using a method where the compiler will create a storage and the value will be placed into it while the identifier of the referencing variable is treated as the identifier for that particular storage location . Below is the demonstration code .
Case 1
#include <iostream>
using namespace std;
int main(void)
{
int num = 5;
const long long &ref = num; //the value 5 is place in a new storage where ref is the identifier
return 0;
}
Case 2:
#include <iostream>
using namespace std;
int main(void)
{
int num = 5;
const int &ref = num + 1; //expression num + 1 evaluated and store in new storage with identifier ref
return 0;
}
Since this 2 cases is valid, how come the case inside The Code: is invalid?
My logic is since the name of the array when used will be converted to pointer to the first element of the array , thus the compiler should've spotted this is not a lvalue and a new storage will be created to store that address along and of course , the referencing variable name will be taken as the identifier for that location .
Note : I know this is slightly out of topic , but may I know whether an array name is Lvalue or not? Just a simple yes or no will do , since changing the code to int &ref = num I assume it's not a lvalue , but I just need further confirmation.
Thank you.
You reference variable is not declared const.
There's a difference between const int * and int * const, and you've picked the wrong one.
Your example (ii) is invalid for the same reason, it should be const int &ref = num + 1;
For your Note, I'm not sure that a simple yes or no will do. A simple array name is an lvalue, referring to the array. However, in most contexts it decays to a pointer-to-first-element, which is an rvalue pointer.