Got confused with const_cast - c++

Sample_Program-1
#include<iostream>
using namespace std ;
int main(){
const int i = 9;
int *j = const_cast<int*>(&i); //Ok
int *j = const_cast<int*>(i); //Error
}
Sample_Program-2
#include<iostream>
using namespace std ;
int main(){
const int i = 9;
int j = const_cast<int&>(i);//Ok
int j = const_cast<int>(i);//Error
}
I was just learning some c++ concept and met with the above 2 concepts . Can anyone please explain the concept i marked as error in the above 2 sample program ?

1) You are casting (const int*) to (int*). So because of const modifier you can't change the value that is placed at that address(pointer point to some address in memory). When you cast it to (int*) compiler will allow change data at that address.
2) You are trying to cast (const int) to pointer to int (int*). (int) and (int*) are different types. This is the same as ask const_cast to cast string to float. The const_cast operator can't change the type of the variable. To make such things you should look to static_cast or reinterpret_cast.
3) You cast const int to reference to int and assign the value to int(you simply copied the value to a new variable). This is probably not exactly you wanted, because changing j in this case doesn't change i. You can create a reference to int instead of j and then you can change the value of i.
4) I don't understand what you are trying to do here. The idea of the const_cast is to remove the const protection on object. So this operation is possible only on pointers and references. You don't need any cast to copy const int to int. But you can't change the value of i until you take pointer or reference and remove the protection.
Conclusion.Removing a const is a bad style of programming. Assume you wrote a library where a function has const int* argument. The user of your library will be sure that his int won't change but you changed it and he lost the data he needed.

When you write
int *j = const_cast<int*>(i);
you are trying to convert 'i' to a pointer. const_cast is not meant to be used for changing the data type.
maybe you meant
int *j = const_cast<int*>(&i);

Here is the first statement explained:
[cast to non-const int pointer] ( [get a pointer to 'i' (const)] );
const_cast<int*> ( &i );
Here is the second statement explained:
[cast to non-const int pointer] ( [get value of 'i'] );
const_cast<int*> ( i );
The error is because an integer value is not a pointer value, and so, the const_cast cannot do that cast. It can only map pointers to pointers, or references to references.
Here is the third statement explained:
[cast to non-const int reference] ( [implicitly get a reference to 'i' (const)] );
const_cast< int& > ( i );
Here is the second statement explained:
[cast to non-const int value] ( [get value of 'i' (const)] );
const_cast< int > ( i );
The error is because the const_cast cannot be used to cast between values, only between pointers or references. For values, we talk about "conversions" not casts. As in:
int i_nc = i; // OK: no need for const-cast since the value is copied.
A conversion is a method to copy the value of an object of one type into an object of another type. Casting operators don't make sense for that purpose.

Related

Is a c++ dereference an lvalue or an rvalue?

I am confident I understand the meaning of rvalue and lvalue. What's not clear to me is whether a dereference is an rvalue.
Consider this:
#define GPIO_BASE 0x20200000
#define GPFSEL1 (*(volatile unsigned int *)(AUX_MU_BASE + 0x4))
GPFSEL1 is a dereference to an unsigned int pointer. That unsigned int pointer is a physical address of a hardware register.
I have read that this is a common technique in bare metal programming to access hardware registers directly. So far I am able to use it with no problem.
Now I want a reference to GPFSEL1 as a member of a struct. Is this possible?
struct MotorControl
{
u8 pwm_pin;
u8 ctrla_pin;
u8 ctrlb_pin;
volatile unsigned int* MYGPFSEL; // this is not the same thing so does not work
}
Given this function below, what's the correct way to reference GPFSEL1 which is defined elsewhere and how to dereference it to set the its value?
MotorContorl group
group.MYGPFSEL = ?
void set(unsigned char pin_number, bool high)
{
if (high)
{
// how to correct this statement
group.MYGPFSEL |= 1<< pin_number;
}
}
"Dereference" is a verb - you dereference a pointer to get access to the value it points to.
The type conversion is straightforward - if you have a pointer to an integer, and you dereference it, you're now working with an integer. It's an lvalue, as it (by construction) takes up a location in memory, and one that you can (usually) modify.
int x = 10;
int* xptr = &x;
*xptr = 5; // *xptr is an lvalue
std::cout << "X: " << x << std::endl; // Prints 5
However, the pointer needs to point to a place in memory. If you just start with
int* xptr;
*xptr = 5;
You're going to get an error, since you're trying to dereference a pointer that doesn't point to a valid memory address. (And if it does, that's purely by coincidence, and you'll incorrectly change the value.)
If you want a reference to GPFSEL1 as a member of your MotorControl struct, you will not be able to initialize the struct without passing it the object to which it will reference. What you probably want instead is a pointer inside the struct, which is much easier to work with:
MotorControl myMotorControl;
myMotorControl.MYGPFSEL = GPFSELF1;
Your current approach will work fine, you just need to initialize the member variable appropriately, e.g.
struct MotorControl control;
control.MYGPFSEL = (volatile unsigned int *)(AUX_MU_BASE + 0x4);
alternatively you can initialize it as
control = &GPFSEL1;
given that you have defined GPFSEL1 as in your original question:
#define GPFSEL1 (*(volatile unsigned int *)(AUX_MU_BASE + 0x4))`
Now you can read the register:
foo = *control.MyGPFSEL;
or set the register:
*control.MyGPFSEL = 123;
This is an easy question to answer, a dereference can be the left-hand side of an assignment so it's an l-value.
Easy case to illustrate:
char sz[] {"hello"};
*sz = 'j';
If something can be the left side of an assignment it is by definition an l-value.

Is it possible to use const_cast on array to change elements?

This is more of an academic question since I know to generally avoid const_cast.
But I was working on the exercise in Chapter 3, # 27 of Thinking in C++, Vol. 1.
Create a const array of double and a volatile array of double. Index
through each array and use const_cast to cast each element to
non-const and non-volatile, respectively, and assign a value to each
element.
I see how to const_cast single vars:
const int i = 0;
int* j = const_cast<int*>(&i);
*j = 1; // compiles and runs
But can't figure out how to get it to work with an array. The following compiles, but throws a 'bad access' exception, as if const is still there.
const int sz = 10;
const double cd[sz] {0,1,2,3,4,5,6,7,8,9};
volatile double vd[sz] {0,1,2,3,4,5,6,7,8,9};
double* cdp = const_cast<double*>(cd);
double* vdp = const_cast<double*>(vd);
cdp[0] = 99; // bad access error
Where have I got it wrong?
Please note:
const int i = 0;
int* j = const_cast<int*>(&i);
*j = 1; // UNDEFINED BEHAVIOR - modify actually const data
You are not allowed to modify an object by casting away constness that was initially declared as const. The compiler may place the contents of the object (or array, in your case) in read-only memory which is enforced at a lower level in the machine than the compiler. When you write, it may trigger a fault. If it's declared const, you must honor that forever, or get crashes as you're experiencing.
It's only ok to modify after casting away constness if the object was initially declared non-const. (That is, the constness was added later, perhaps as a reference parameter to a function.)
Let's rephrase your question into at least a valid situation for the sake of example.
// f() takes a reference to an array, but add constness
void f(const double(&arr)[10])
{
// Here it's ok to cast away constness and modify since the
// underlying object isn't declared const (though in general a
// function doesn't know that about its caller, except in
// constrained situations.)
double * array = const_cast<double*>(arr);
array[1] = 99;
}
int main()
{
// NOTE: array is NOT CONST
double arr[10] {0,1,2,3,4,5,6,7,8,9};
f(arr);
}
And this is fine.

Why a constant integer pointer point to a non constant integer allowed?

I'm currently trying to learn some C++ and came across following unintuitive behavior. As t is a pointer to a const int I would expect *t to stay the same as long as we do not change t.
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int a = 3;
const int* t = &a; //Why is this allowed? a is NOT const int
a = 4;
cout << *t << endl; //This does then print 4
//*t = 4; //Throws error.
return 0;
}
Can anyone explain why this is does compile?
const int* t just means you can't change the value t pointing to by t, nothing more. The original value might be changed, but it has nothing to do with t's responsibility.
If you want to ensure the value won't be changed, you should let t point to a const, such as
const int a = 3;
const int* t = &a;
And for this case, you can't make a int* pointer point to it.
int* t = &a; // error
As t is a pointer to a const int I would expect *t to stay the same as long as we do not change t.
You cannot make that assumption in general, because t might point to a non-const object, such as in your example.
const int* t = &a; //Why is this allowed? a is NOT const int
Can anyone explain why this is does compile?
The rules of c++ allow the implicit conversion of T* to const T*. It is allowed, because it's very useful to have a pointer-to-const (or reference) to an non-const object. Pointer to const simply means that the object cannot be modified "through" the pointer. The object itself could be const, or non-const.
As an example of why it is useful, you could have some modifiable state as a private member of an object, and return const view to it so that others can observe, but not modify. A practical example of such is std::string::c_str(). It returns a const char* even though the internal buffer of the std::string is non-const.
As t is a pointer to a const int I would expect *t to stay the same as long as we do not change t.
The answer is simple: the const keyword in a pointer type declaration does not mean 'it is constant' but rather 'you are not allowed to modify it'.
This is useful for example when you create a variable and then call another function to do something with it, but you want to forbid modification:
extern int countZerosInArray( const int *array, int arrayLen);
int myVariableArray[ 100 ];
// ... fill the array - i.e. modify it!
int noOfZeros = countZerosInArray(myVariableArray, 100);
The countZerosInArray function is told it has read-only access to the array, although the array itself of course is not constant.
const int* t = &a; // int* or const int* can assgin to const int*

pass by reference : no known conversion for argument 6 from 'int' to 'int&'

I have a function where I pass an argument by reference since I expect the function to edit it. This function is called at several places and I only care about the ref value when called at a particular instance .
Pseudocode:
test_fn(int a, int b, inc , int d, int e, int& ref)
{
//bunch of other functionalities
//.
//.
ref = (a*b+c)*(d+e);
}
test_fn(1,2,3,4,5,0)//everywhere that I do not care about ref
int value = 0;
test_fn(1,2,3,4,5, value)//I care about value here and would use it in the remainder of the code .
Why can I not pass a 0 directly ? I tried passing a NULL as well and that has a long int to an int conversion error.
Why is this wrong ? And what is the best way to achieve the expected outcome here?
A regular int& means that it needs to be assigned to a variable already; it needs to be an lvalue.
0 is not assigned to a variable; it's a "free variable," which means that it's unattached to a label. This means that it's an rvalue, a temporary variable that is not bound to a variable. It's denoted by int&&.
rvalues can be converted to lvalues if you make it const int&. It makes sense that a constant can be converted to a reference to int constant (reading right to left).
However, that would be pointless, as you want to modify the variable; therefore, the answer is to follow your own convention and don't pass in things that are not already in "existence" and bound to a label/name, like constants or moved variables.
Consider this much simpler example:
test_fn(int& ref)
{
ref = 3;
}
int main() {
test_fn(0);
}
This is effectively trying to set 0 to 3. i.e:
int main() {
0 = 3;
}
But that's nonsense. An int & (as opposed to a const int&) can only accept something that is modifiable.
(As #nogeek001 points out, const int& wouldn't allow us to modify ref anyway.)
in order to pass variable by reference it has to exist, passing 0 or NULL means you're sending in a constant. You cannot edit the value of a constant, as it is actualy not a variable.
As for solving your problem, you probaby should use pointers to achieve that, then check if the pointer is set to 0, NULL or if you use C++11, nullptr
As others have stated, you can't pass a literal as a reference.
What you can do is pass an address, and then check if it is NULL within the function:
test_fn(int a, int b, inc , int d, int e, int* ref)
{
int someValue = (a*b+c)*(d+e);
if ( ref )
*ref = someValue;
}
//...
test_fn(1,2,3,4,5,0);
int value = 0;
test_fn(1,2,3,4,5, &value)
ref is a reference not a pointer. 0 may be passed if it were, meaning a pointer to null; reference can't point to nothing and must be bound to an lvalue.

C++ pointer to constant error

In a book I'm reading about C++ (C++ for Dummies) there is a section that says the following:
int nVar = 10;
int* pVar = &nVar;
const int* pcVar = pVar; // this is legal
int* pVar2 = pcVar; // this is not
The book then goes on to explain:
The assignment pcVar = pVar; is okay -- this is adding the const
restriction. The final assignment in the snippet is not allowed since
it attempts to remove the const-ness of pcVar
My question is why is the last line not "legal". I don't understand how that impedes on the "const-ness" of pcVar. Thanks.
const int *pcVar = pVar;
int *pVar2 = pcVar;
If pcVar is const int *, that implies that the int it points to may be const. (It isn't in this case, but it may be.) So if you assign pVar2, which is a non-const int *, it still allows the int it points to to be modified.
So if pcVar actually pointed to a const int, and you assign an int * to its address, then that int * pointer (pVar2 in this case) will allow you, by dereferencing, to modify it, and that's illegal (it's a constraint violation, so it invokes undefined behavior).
All the compiler knows is that pcVar is a const int*. That is, it points to a const int. Just because you made it point at a non-const int doesn't matter. For all the compiler knows, the pointer value could have changed at some point to point at a truly const int. Therefore, the compiler won't let you convert from a const int* back to a int* because it would be lying about the constness of the object it was pointing at.
For a simpler example, consider:
const int x;
const int* pc = x;
int* p = pc; // Illegal
Here, x truly is a const int. If you could do that third line, you could then access the const int object through p (by doing *pc) and modify it. That would be bad - x is const for a reason.
However, in the example you gave, since you know that the original object was non-const, you could use const_cast to force the compiler into trusting you:
int* pVar2 = const_cast<int*>(pcVar);
Note that this is only valid if you know for certain that the object is non-const.
It's just saying you can't create a non-const pointer from one that was const (at least, not without a const_cast).
The idea behind const is to have objects that cannot be modified by accident. Getting rid of const through a simple assignment would be quite dangerous, and would allow things like this:
void function(int* m) {
*m = 20;
}
int main() {
const int x = 10;
//Oops! x isn't constant inside function any more, and is now 20!
function(&x);
}
Also, please check out The Definitive C++ Book and Guide List, it has lots of great references (C++ for dummies doesn't quite make the cut).
Mixing const and non-const is illegal. The reason being, if you tell the compiler that one location's value is const and then use another pointer to modify that value, you have violated the const contract you made with the first element.
pcVar stays the same but pVar2 points to a non-const, const can be added but not taken away. The compiler does not look at the original nVar being non-const, only the attempt to assign a const to a non const. Otherwise you could get around the const and change the value.
int * pVar = &nVar;
*pVar = 4 //is legal
const int* pcVar = pVar; // this is legal
*pcVar = 3 // this is not legal, we said the value was const thus it can not be changed
int* pVar2 = pcVar; // this is not legal because...
*pVar2 = 3 -> *pcVar = 3
The second line
int pVar = &nVar;
is error.
g++ compiler says.
error: invalid conversion from ‘int*’ to ‘int’