Why are top level Constants ignored while copying objects? [closed] - c++

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I am reading C++ primer and I'm stuck on this topic. It is written that
int i=0;
const int ci=42; //const is top level.
const int *p2=&ci; //const is low level.
const int *const p3=p2; //rightmost const is top level,left one is low level.
int *p=p3 //error.
p2=p3 //ok:p2 has the same low level constant qualification as p3.
int &r=ci; //error: can't bind an ordinary int to const int object
const int &r2=i; //ok:can bind const int to plain int.
Now if top level constants are ignored in last statement, then it should give an error, because the low level constant qualification of &r2 and i are not same. The why is the last staement correct??

You're asking a billion questions in one, but I'll summarize.
These:
int &r=ci; //error: can't bind an ordinary int to const int object
const int &r2=i; //ok:can bind const int to plain int.
Follow the rules of reference initialization. The left-hand side needs to have the same or greater than cv-qualification of the right-hand side.
These:
const int *p2=&ci; //const is low level.
const int *const p3=p2; //rightmost const is top level,left one is low level.
int *p=p3 //error.
p2=p3 //ok:p2 has the same low level constant qualification as p3.
Follow the rules of qualification conversions. Essentially they try to preserve const correctness. Which an assignment like p = p3 would certainly not do.
I think you'll have a much easier time comprehending the rules if you drop the "top-level" and "low-level" const stuff as they're clearly not helping you understand what's happening here.

When working through const declarations, you look backwards. Unfortunately it has one exception: const int is allowed for historical reasons, but it means the same as int const.
Below, the const "looks back" to the int so p points to an int which can't be changed.
int const * p;
And here, the const "looks back" to the int * so p is not allowed to point to anything else, but what it points to now can be changed through p.
int i = 5; int j = 5;
int * const p = &i;
*p = 6; // ok, you can still modify `i` through `p`
p = &j; // error because you can't change what `p` points at
And here, p is a const pointer to a const int. p is not allowed to change and what it points to cannot be changed.
int i = 5; int j = 5
int const * const p = &i;
*p = 6; // error, promised not to makes changes through `p`
p = &j; // error `p` is stuck to `i`
When assigning one thing to another, the rule is that promises cannot be broken. int const is a promise that the int will never be changed. If you try this:
int const i = 5;
int * p = &i; // error
and the compiler let you do it, you would then be able to break the promise that i would never change by doing this
*p = 6;
and now i will be changed from 5 to 6, breaking the promise that i will never change.
It's ok the other way round because you're not breaking any promises. In this case, you're only promising not to change i when you access it through the pointer p
int i = 5;
int const * p = &i;
*p = 6; // error, because we promised not to change `i` through `p`
i = 6; // ok, because `i` itself is not const
const is important as a built-in safety check. The compiler will give you an error if something you declare as const is changed. You can declare methods of a class to be const and that is a promise that no data in the class will be changed by that method. It's easy to forget and later modify that method to change something in the class but the compiler will remind you.
It is also important for optimisation. If the compiler knows that something is const it can make a lot of simplifying assumptions to speed up the code.

Related

auto type specifier ignores top-level const

I stumbled upon this question while reading "C++ Primer", by Lippman et al. (5/e)
14 int i = 0;
15 const int ci = i, &cr = ci;
16 auto c = cr;
17
18 c = 12; // works fine
we have this code snippet.
in line 15
const on ci is top-level, const on cr is (as is always on references) is low-level.
Pg. 69 of this book goes to say,
"auto ordinarily ignores top-level consts"
But it is ignoring low-level const on cr as c is of type int (value of c can be changed to 12 without compiler complaining).
Whereas I expected c to be of the type const int as there is a low-level const on cr.
Please help me understand this.
Think of the top-level as it relates to the resulting type of the auto variable. If it was going to be const int it instead will be int.
If it was going to be const int* const it instead will be const int*.
In simple terms, the top level const is the one that applies to the object itself. A int * const is not const, it is a non-const pointer to a const, whereas a top level const as in const int * const makes the object itself const.
The rules are designed to be useful. Removing the top most const makes auto applicable in such common cases:
const int x = 5;
auto y = x;
++y;
Usually C++ defaults to non-const. You need to specifiy it when you want to declare y as const.

Changing the address a pointer is pointing to [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I want to change different pointer (different types), that they point to NULL/0/nullptr..., but I have to use a variable to do this! Generally I understand pointers and I know what the problem is, but I don't know how to solve it.
So I need something like
int NULLPOINTER = nullptr; // data type can change if needed
int* myIntPointer = NULLPOINTER;
float* myFloatPointer = NULLPOINTER;
foo* myFooPointer = NULLPOINTER;
As shown above, this is not possible because of the invalid conversion error (int to int*/float*/foo*).
So how can I archiv this?
As requested a more complex example for clarification:
class foo
{
float floatVar;
};
class bar
{
char charVar;
};
void changeSomething(bar* pBarTmp)
{
/* pGlobal is a member of another class and the type is depends on the method */
pGlobal = pBarTmp;
}
int main()
{
/* Working just fine */
int var = 1;
int *pointer = &var;
/* Also working fine */
foo *pointer = 0; //nullptr or NULL or __null (compiler dependend)
/* Not working because newAddress is int and not bar* / foo* / int* */
int newAddress = 0;
// Only one of the following is present, it depends on the
// method/class (just for visualization)
bar *pointer = newAddress;
foo *pointer = newAddress;
int *pointer = newAddress;
changeSomething (pointer);
}
I am not allowed to change int newAddress; into int* newAddress;. Overall I won't use something else than NULL / 0 / nullptr / ...
Another difficulty is, that I cannot use reinterpret_cast cause of coding guidelines.
int* p3 = 1; //Error invalid conversion from int to int
You can directly set the address of a pointer in C++
int* p3 = reinterpret_cast<int*>( 0x00000001 );
But its not good idea since you dont know where the pointer points in memory and de-referencing would lead to undefined behavior.
Its invalid conversion because 1 has type int and it's not possible to assign to pointer variable int * without cast.
int* p2 = 0; //p is a NULL-pointer
Pointers pointing to null should be initialized
int* p2 = nullptr;
instead of
int* p2 = 0;
Since C++11 you can create an instance of std::nullptr_t and assign it to pointers.
std::nullptr_t initAddr;
int* p2 = initAddr;
C++11 came with nullptr, and nullptr has type std::nullptr_t. You can create an instance of this and assign:
int* p1;
char* p2;
double* p3;
std::nullptr_t newAddress;
p1 = newAddress;
p2 = newAddress;
p3 = newAddress;
Demo
This satisfies having your NULL in a variable.
Changing the adress a pointer is pointing to
My goal is to change the adress of different pointer types to NULL
By assignment:
int* p = nullptr;
and I have to use a variable to do this.
int newAdress = 0;
int* p2 = newAdress;
The bug here is that newAdress has the wrong type. The compiler helps you out by telling you that is wrong. You will need to use a pointer (of compatible type) variable to assign a pointer:
int* newAdress = nullptr;
// ^ see here, a pointer
int* p2 = newAdress;
int* p3 = 1;
This assignment makes little sense in most cases. 1 is not null, nor is there usually a guarantee that there is an int object at that memory location.
Nevertheless, there are some special cases where you need a particular address value. That can be achieved with casting:
int* p3 = reinterpret_cast<int*>(1);
If you don't know of such cases, then you don't need to do it. If you don't have such case, then using pointer to the arbitrary memory location has undefined behaviour.
You have to understand that int* ≠ int. You shouldn't directly change the address. That seems pointless, since every program run variables are saved in different parts of memory with different addresses.
It only works with NULL, which is the same as 0, but should be avoided in C++ 11, where you should use nullptr for safety.
What you can do is set the address with the address-of-operator &:
int a;
int* b = &a;

Rules for converting between pointer types where cv-qualifiers are the only difference

This question specifically relates to C++98, but feel free to pitch in any useful info w/regard to newer standards if you like.
In case you know the answer and want to skip the rest, the short & sweet of it is:
int **w;
int volatile* *x = w; // error
int volatile*const*y = w; // OK
int const *const*z = w; // OK
Why would const be necessary to the right of volatile in the declaration of y? What possible evil could someone accomplish if the declaration for x were allowed?
In section 4.4.4 of the standard it says:
A conversion can add cv-qualifiers at levels other than the first in multi-level pointers, subject to the following rules:
Two pointer types T1 & T2 are similar if there exists a type T and integer n > 0 such that:
T1 is CV10 ptr to CV11 ptr to ... CV1N T
T2 is CV20 ptr to CV21 ptr to ... CV2N T
... where each CVij is const, volatile, const volatile, or nothing. The n-tuple of cv-qualifiers after the first in a pointer type, e.g., CV11, CV12, ..., CV1N in the pointer type T1, is called the cv-qualification signature of the pointer type. An expression of type T1 can be converted to type T2 iff the following conditions are satisfied:
the pointer types are similar
for every j > 0, if const is in CV1j, then const is in CV2j, and similarly for volatile.
if the CV1j and CV2j are different, then const is in every CV2k for 0 < k < j
... after which it goes on to give an example for assigning a ** to a const**. The emphasis above is mine, italics are from the document.
Putting this into code:
int CV13* CV12* CV11* CV10 b1;
int CV23* CV22* CV21* CV20 b2 = b1;
I'm a little fuzzy on some of the details ... so here's some questions or potentially flawed observations:
1) It says at levels other than the first; this isn't elaborated on any further, but CV20 can be any valid CV qualifier.
2) The 3rd rule at the bottom says if T2 adds either const OR volatile at level j, then levels 1 ... j-1 must be const (or suffer the wrath). In the following, the # of stars differs from the one at the top to emphasize what the 3rd rule says:
int *****w;
int **volatile* * *x = w; // error
int **volatile*const*const*y = w; // OK
int **const *const*const*z = w; // OK
I understand why it's needed for z, but why with y? Here's roughly what the example in 4.4.4 looks like, modified for the volatile case:
void f( int **x ) {
int volatile**y = x; // not allowed
// do some evil here
}
What evil could be put there?
(Note: "This question specifically relates to C++98" but the state of affairs is the same in all versions of the Standard, past and present (and future too I would bet), because it's essentially about const-correctness and preventing coders from opening a hole in the type system.)
As the Standard uses the general term "cv-qualifiers", I find it easier to understand when reasoning only with "const" (no "volatile") [but see further below for an example with volatile]. The "C++ FAQ Lite" has a related entry : Why am I getting an error converting a Foo** → Foo const**?
Essentially, allowing the conversion would let you silently modify a const T (through a pointer to non-const T):
int const theAnswer = 42;
int* p = 0; // int* p = &theAnswer; is not allowed of course...
int** pp = &p;
int const** ppc = pp; // <-- Error, but imagine this were allowed...
*ppc = &theAnswer; // &theAnswer is `int const*` and *ppc is `int const*` too,
// but it's also doing *pp = &theAnswer; i.e. p = &theAnswer;
*p = 999; // I.e. theAnswer = 999; => modifying a const int!
But by adding a "first-level" const, the conversion int const* const* pcpc = pp; is valid because the compiler will prevent you doing *pcpc = &theAnswer; afterwards (because *pcpc is const).
Edit: As for volatile, the problem is maybe less obvious than with const, but allowing the conversion would let you silently incorrectly access (read or write) a volatile T as if it were not volatile (through a pointer to non-volatile T):
extern int volatile externTimer;
int* p = 0; // int* p = &externTimer; is not allowed...
int** pp = &p;
int volatile** ppv = pp; // <-- Error, but imagine this were allowed...
*ppv = &externTimer; // &externTimer is `int volatile*` and *ppv too,
// but it's also doing *pp = &externTimer; i.e. p = &externTimer;
int a1 = externTimer; // First read
int a2 = externTimer; // Second read, mandatory: the value may have changed externally
int b1 = *p; // First read
int b2 = *p; // Second read? may be optimized out! because *p is not volatile
But by adding a "first-level" const, the conversion int volatile* const* pcpv = pp; is valid because the compiler will prevent you doing *pcpv = &externTimer; afterwards (because *pcpv is const).

non-const pointer argument to a const double pointer parameter

The const modifier in C++ before star means that using this pointer the value pointed at cannot be changed, while the pointer itself can be made to point something else. In the below
void justloadme(const int **ptr)
{
*ptr = new int[5];
}
int main()
{
int *ptr = NULL;
justloadme(&ptr);
}
justloadme function should not be allowed to edit the integer values (if any) pointed by the passed param, while it can edit the int* value (since the const is not after the first star), but still why do I get a compiler error in both GCC and VC++?
GCC: error: invalid conversion from int** to const int**
VC++: error C2664: 'justloadme' : cannot convert parameter 1 from 'int **' to 'const int **'. Conversion loses qualifiers
Why does it say that the conversion loses qualifiers? Isn't it gaining the const qualifier? Moreover, isn't it similar to strlen(const char*) where we pass a non-const char*
As most times, the compiler is right and intuition wrong. The problem is that if that particular assignment was allowed you could break const-correctness in your program:
const int constant = 10;
int *modifier = 0;
const int ** const_breaker = &modifier; // [*] this is equivalent to your code
*const_breaker = & constant; // no problem, const_breaker points to
// pointer to a constant integer, but...
// we are actually doing: modifer = &constant!!!
*modifier = 5; // ouch!! we are modifying a constant!!!
The line marked with [*] is the culprit for that violation, and is disallowed for that particular reason. The language allows adding const to the last level but not the first:
int * const * correct = &modifier; // ok, this does not break correctness of the code

Initializing pointers in C++

Can assign a pointer to a value on declaration? Something like this:
int * p = &(1000)
Yes, you can initialize pointers to a value on declaration, however you can't do:
int *p = &(1000);
& is the address of operator and you can't apply that to a constant (although if you could, that would be interesting). Try using another variable:
int foo = 1000;
int *p = &foo;
or type-casting:
int *p = (int *)(1000); // or reinterpret_cast<>/static_cast<>/etc
What about:
// Creates a pointer p to an integer initialized with value 1000.
int * p = new int(1000);
Tested and works. ;-)
There are two things not clear in the question to me. Do you want to set the pointer to a specific value (i.e address), or do you want to make the pointer point to some specific variable?
In the latter case, you can just use the address-of operator. The value of the pointer is then set to the address of some_int_variable.
int *p = &some_int_variable;
*p = 10; // same as some_int_variable = 10;
Note: What follows is evil changing of the pointer's value manually. If you don't know whether you want to do that, you don't want to do it.
In the former case (i.e setting to some specific, given address), you can't just do
int *p = 1000;
Since the compiler won't take the int and interpret it as an address. You will have to tell the compiler it should do that explicitly:
int *p = reinterpret_cast<int*>(1000);
Now, the pointer will reference some integer (hopefully) at address 1000. Note that the result is implementation defined. But nevertheless, that are the semantics and that is the way you tell the compiler about it.
Update: The committee fixed the weird behavior of reinterpret_cast<T*>(0) that was suggested by a note and for which i provided a workaround before. See here.
Um. I don't think you can take a non-const address of a constant like that.
but this works:
int x = 3;
int *px = &x;
cout << *px; // prints 3
or this:
const int x = 3;
const int *px = &x;
const int *pfoo = reinterpret_cast<const int*>(47);