when ptr is a const pointer and also a pointer to const int, how will it behave while copying its value into other pointers to const int or const pointers? Will be top-level or low-level?
when you use:
int var=8;
int const *low = &var; //Low level const
const int *const high_low = low; //Both High & Low level const present
In the above code, both high_low & low behaves as Low-level const.
But in this code:
int var=8;
const int *const high_low = &var;
int const *low=&var;
high_low = low; //Error: high_low behaves as High-Level const where as Low is Low-Level const
The problem is that you are seeing it in terms of high & low level const rather than whether a variable can be changed or not - that solves the problem
Related
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.
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.
The possibility to create a array literal on read-only memory, exists as the string literal, but doesn't look to extend to other types.
const char* const kChar1{"This is a name"};
const char kChar2[]={"This is a name"};
const int* const kInt1{5,3,2,6,9,0,0,2}; //error
const int kInt2[]{5,3,2,6,9,0,0,2};
I can't create KInt1, like I created kChar1.
How could I create the equivalent?
This is fairly close:
const int kInt2[]{5,3,2,6,9,0,0,2};
const int* const kInt1 = kInt2;
The only real difference is that kInt1 will necessarily point to the same memory as kInt2, but kChar1 does not necessarily point to the same memory as kChar2.
I think this is correct
const int one = 5;
const int two = 10;
const int* const kInt1[] ={&one,&two};
I have a class with a 2D array of ints implemented as an int**. I implemented an accessor function to this 2D array as follows, returning a const int** to prevent the user from being able to edit it:
const int** Class::Access() const
{
return pp_array;
}
But I got the compilation error "invalid conversion from int** to const int**". Why is a promotion to const not allowed here? How can I give the user access to the information without editing rights?
Greyson is correct that you'll want to use const int* const*, but didn't explain why your original version failed.
Here is a demonstration of why int** is incompatible with const int**:
const int ci = 0;
const int* pci = &ci;
int* pi;
int** ppi = π
const int** ppci = ppi; // this line is the lynchpin
*ppci = pci;
*pi = 1; // modifies ci!
I was mistaken about the constness of the method being the reason for the error. As Ben points out, the const-ness of the method is irrelavent, since that applies only to the value of the exterior pointer [to pointers to ints], which can be copied to a mutable version trivially.
In order to protect the data (which is your preferred outcome) you should make both the ints and the pointers to ints constant:
int const * const * Class::Access() const
{
return pp_array;
}
Will work.
If you prefer to have the const in front you can also write the declaration like so:
const int * const * Class::Access() const;
but since the second const applies to the pointers, it must be placed to the right (like the const which applies to the method) of the asterisk.
From my understanding, const modifiers should be read from right to left. From that, I get that:
const char*
is a pointer whose char elements can't be modified, but the pointer itself can, and
char const*
is a constant pointer to mutable chars.
But I get the following errors for the following code:
const char* x = new char[20];
x = new char[30]; //this works, as expected
x[0] = 'a'; //gives an error as expected
char const* y = new char[20];
y = new char[20]; //this works, although the pointer should be const (right?)
y[0] = 'a'; //this doesn't although I expect it to work
So... which one is it? Is my understanding or my compiler(VS 2005) wrong?
Actually, according to the standard, const modifies the element directly to its left. The use of const at the beginning of a declaration is just a convenient mental shortcut. So the following two statements are equivalent:
char const * pointerToConstantContent1;
const char * pointerToConstantContent2;
In order to ensure the pointer itself is not modified, const should be placed after the asterisk:
char * const constantPointerToMutableContent;
To protect both the pointer and the content to which it points, use two consts.
char const * const constantPointerToConstantContent;
I've personally adopted always putting the const after the portion I intend not to modify such that I maintain consistency even when the pointer is the part I wish to keep constant.
It works because both are same. May be you confused in this,
const char* // both are same
char const*
and
char* const // unmutable pointer to "char"
and
const char* const // unmutable pointer to "const char"
[To remember this, here is a simple rule, '*' affects its whole LHS first]
That is because the rule is:
RULE: const binds left, unless there is nothing on the left, then it binds right :)
so, look at these as:
(const --->> char)*
(char <<--- const)*
both same! oh, and --->> and <<--- are NOT operators, they just show what the const binds to.
(from 2 simple variable initialization question)
A really good rule of thumb regarding const:
Read Declarations Right-to-Left.
(see Vandevoorde/Josutiss "C++ Templates: The Complete Guide")
E.g.:
int const x; // x is a constant int
const int x; // x is an int which is const
// easy. the rule becomes really useful in the following:
int const * const p; // p is const-pointer to const-int
int const &p; // p is a reference to const-int
int * const * p; // p is a pointer to const-pointer to int.
Ever since I follow this rule-of-thumb, I never misinterpreted such declarations again.
(: sisab retcarahc-rep a no ton ,sisab nekot-rep a no tfel-ot-thgir naem I hguohT :tidE
Here is how I always try to interpret:
char *p
|_____ start from the asterisk. The above declaration is read as: "content of `p` is a `char`".
char * const p
|_____ again start from the asterisk. "content of constant (since we have the `const`
modifier in the front) `p` is a `char`".
char const *p
|_____ again start from the asterisk. "content of `p` is a constant `char`".
Hope it helps!
In both of your cases you're pointing to a constant char.
const char * x //(1) a variable pointer to a constant char
char const * x //(2) a variable pointer to a constant char
char * const x //(3) a constant pointer to a variable char
char const * const x //(4) a constant pointer to a constant char
char const * const * x //(5) a variable pointer to a constant pointer to a constant char
char const * const * const x //(6) can you guess this one?
By default, const applies to what is inmediately at is left, but it could apply to what is inmediately at its right if there's nothing preceeding it, as in (1).