Notion of top-level and low-level const - c++

We can define any object to be a low-level const by putting the const before the base type. If a top-level const is defined by placing const after an objects declarator. So this immediately makes sense for pointers since something like int *const p = i defines p be a const pointer to an int. This distinction is clear. However I am wondering if such a distinction can be made for references. I have already tried to compile something as follows,
int main(void)
{
int i = 40;
int &const r = i;
}
which returns a compile time error. However it got me thinking why? If the purpose of low-level const is to ensure the address being pointed to cannot be changed then why is this not possible for a reference to be a low-level const since it by definition cannot be bound to a different object after initialisation. Should we not consider all references by default as low-level const? If not, why?

Related

What is the meaning of the statement below?

I was going through C++ primer, and I am confused in line 2
const int ci = 42; // we cannot change ci; const is top-leve
int i = ci; // ok: when we copy ci, its top-level const is ignore
As per me, we defined ci to be constant. Then we simply copy it in 'i'
How does the "top-level const is ignored" come into the picture?
Can someone explain what exactly is difference between top & low-level const?
The value stored in the variable defined as const int ci remains a constant for its entire lifetime. This means you could not change its value in future lines of code. (ie. You could not later write ci = 43;, the compiler would enforce the const and not allow this code to build.)
However, when copied its value to int i, you created a new variable named i. And i was not defined as const, so it is what we refer to as mutable, meaning the code that follows can change its value.
The top-level term is used by some people and is dragged in from script languages. I saw it used in some C++ primers in meaning that "object itself is const" while const int* p would be low-level const, i.e. referred object is const.
const int* p; // low-level const, a pointer to a const
int* const p = &i; // top-level const, pointer is const
This term is not officially defined and perhaps should avoided due to confusion it creates.
const int* const p = &ci; // top-level and low-level?
C++ by default creates and operates upon mutable data, so those terms have little value . If you initialize a non-const object with a const one, new copy is mutable unless this is a class type and there is an intentional code that does otherwise in user-defined constructor. Created object still will not be const unless declared so. Also C++ enjoys existence of const_cast conversion which would be an affront to languages like Clojure.

Why no const reference in C++ just like const pointer?

int main()
{
int n = 1;
int* const p = &n; // ok
*p = 2; // ok as expected.
p = 0; // error as expected.
int& const m = n;
// error: 'const' qualifier may not be
// applied to a reference
return 0;
}
Why no const reference in C++ just like const pointer?
What's the rationale behind the design?
References in C++ differ from pointers in several essential ways. One of the difference is:
Once a reference is created, it cannot be later made to reference another object; it cannot be reseated. This is often done with pointers.
It means Reference are like similar (see the link at the end of this answer) to const pointer (not pointer to a const!) in C++...
int a = 5;
int& m = a; // Behaves similar to int * const m = &a;
// See the link at the bottom for the differences between const pointer and reference.
and hence, you can't change/rebind them to point to some other address. So, you don't need a explicit const qualifier for a reference and that's why it is disallowed by the compiler.
See this link to learn Why are references not reseatable in C++?. I have copied the accepted answer of the above link:
The reason that C++ does not allow you to rebind references is given in Stroustrup's "Design and Evolution of C++" :
It is not possible to change what a reference refers to after initialization. That is, once a C++ reference is initialized it cannot be made to refer to a different object later; it cannot be re-bound. I had in the past been bitten by Algol68 references where r1=r2 can either assign through r1 to the object referred to or assign a new reference value to r1 (re-binding r1) depending on the type of r2. I wanted to avoid such problems in C++.
EDIT:
See this link for Difference between const pointer and reference? (Thanks to #M.M for pointing out the ambiguity in my statement).
Why no const reference in C++ just like const pointer?
References cannot be modified. Adding const qualification to non-modifiable entity would be meaningless and confusing.
Note that it is technically possible to apply const to a reference indirectly through a type alias or template type argument. Example:
T some_t;
using Ref = T&;
Ref const some_ref = some_t; // well-formed
Ref const type "collapses" into T&, and is same as unqualified Ref. I recommend to generally avoid creating type aliases for pointers and references, except for rare cases where they are conventional. Specifically, Container::reference type alias and similar are conventional.
int& const m = n;
IMHO because it's inherently constant by compiler nature, just
int n ;
n has ininherent constant reference
so as it is parsing codes it just determine to whichever place const qualifier is only allowed being there, by a compiler rule method for parsing, if not allowed then go to error/warning

Why can const int& bind to an int?

In the C++ primer, I found that const int & can bind with a int object.I don't understand that,because I think const int & should bind with a const int not a int object, the int object can change, the book explain this question for that when the const int & object bind with int; there is a temporary object between the two, for example:
int a=0;
const int &r=a;
We can use b as the temporary value, so above is equal that:
const int b=a;
const int &r=b;
But I think the book is not right, because if there is a temporary like b existing between a and r,the value of r can't be changed, but when I debug the following coding in visual studio, I found it is not right:
int a=0;
const int &r=a;
a=3;
cout<<r<<endl;
The output is that r=3; the value of r can be changed, why? I don't understand that.
don't understand that,because I think const int & should bind with a const int not a int object
You are mistaken. A reference-to-const (const reference in short) doesn't mean that only const objects can be bound. It means that the object can not be modified through the reference.
What is not allowed would be to bind a non-const reference to a const object, because such reference could be used to modify the object which would break the constness.
the value of r can be changed, why?
The object that r refers to was modified - which is OK because the object isn't const. r still refers to the same object and the object was not modified using r.
Having a const reference does not mean that the referred object can not change. It means that the object can not be changed using that reference.
If you wish to have an object that can not change, then that object itself has to be const. A const reference does not make the referred object const. The book has shown you how to create a const object:
const int b=a;
I have regarded reference as poiter mistakely,because they are similiar some time.
Indeed, references are very similar to pointers. Regarding the context of this question, they behave similarly. A demo:
int a=0;
const int *r=&a;
a=3;
cout<<*r<<endl;
const in this example only guarantee that a can not be changed where r is used. Usually this is used for functions that do not change input parameters like:
int doNotModifyFoo(const int &foo);
An object cannot be modified "through" a const reference or a pointer to const of it. But a const reference or pointer to const doesn't impose any kind of run-time lock on the underlying object and (assuming it was not declared const) the original declaration or any non-const reference or pointer to non-const can still be used to modify it.
Indeed if the object was not originally declared const then const-ness can be cast away and used to modify the underlying object.
Indeed even if the object was originally declared const this may be possible (but is implementation dependent).
const is a way of indicating that a function (or functions) shouldn't modify an object (either as an argument in our return value out or declaration).
It doesn't (in general) mean the object can't be modified through that or some other reference/pointer.
It also not possible to modify what a reference refers to. Or at least there is no valid way of doing it. In some circumstances it is possible in practice by jiggery pokery of obtaining an address of where a reference is held and modifying it like a pointer. That will usually fail in some circumstances because references are often optimized out of existence and because the compiler knows they 'cannot be modified' such violations might only be partially successful.
In C++, you can refer to non-const objects with references and/or pointers to const.
int x = 10;
const int& r = x; //OK
const int* p = &x; //OK
Of course, since x is not constant it can be changed. However, what you're basically saying by having a const reference to a non-const object is: I will not change this object via this reference/pointer. You still can change the object directly or through other references or pointers.
Think of it as a read-only handle. Yes, the object itself may be mutable, but in many cases you may be willing to acquire and/or provide only a read-only access to that otherwise mutable variable.
int a=0;
const int &r=a;
a=3;
cout<<r<<endl;
The output is that r=3; the value of r can be changed, why? I don't
understand that.
The const only applies to the reference r. It means that you can't change whatever r binded to via r. You can't do:
r = 3;
That will be an error, because r is a const int& and cannot be modified. For now you can think of references as some sort of lightweight transparent "proxy" of an object. So if the "proxy" is const, you cannot modify the object through the "proxy". However, it doesn't mean the original object cannot be modified.
Basically, const int & r promises not to mutate the value it's referencing. This is a stronger guarantee than int &. So, it's possible to refer to an int using a const int & reference. But you may not modify it. It's a subset of the operations possible on the value. However, it's not true the other way around, if you were trying to get a int & reference to an const int value, it would result in a compiler error because the value itself is immutable, and you are trying to get a mutable reference to this immutable value. The operations possible on the int & reference are a superset of what's possible on const int value.
The value of the variable a which is 0 initially is holded at memory.
If you write later: a=5, it'll set that value in memory to 5.
When you write a reference const int & r = a; you are only saying that r should access that same place in memory where 5 is being hold.
Because a is not const it can modify the value.
This is basically how it works.
There are two things:
Const and reference.
Reference is just another name for same memory unit. You can use any name to change the value held at that memory.
A const reference is denoted in C++ by a code like
int j =2;
int const &i = j; //or Alternatively
const int &i = j;
There is no restriction which makes the referred object to be constant as well.
Here, you can not use i to change value of j. However this does not make the memory location j to be constant.You can peacefully change the value of j.
j =3
//but i = 3 will raise an error saying
//assignment of read-only reference ā€˜iā€™

const to Non-const Conversion in C++

I'm really annoyed by const keyword these days, as I'm not quite familiar with it. I had a vector that stores all const pointers like vector<const BoxT<T> *> *Q_exclude, and in the constructor of another class, I need an element in this queue to be passed in as a parameter and assign it to a non-const member. My question is:
How do I assign a const variable to a non-const variable? I know this doesn't make sense because after all, a const is a const, and should not be changed by any mean. But that annoying member variable REALLY has to be changed during the process! I might also change the data type in the vector to be non-const, but that would be too much work. Or does anyone know how to avoid such situation?
You can assign a const object to a non-const object just fine. Because you're copying and thus creating a new object, constness is not violated.
Like so:
int main() {
const int a = 3;
int b = a;
}
It's different if you want to obtain a pointer or reference to the original, const object:
int main() {
const int a = 3;
int& b = a; // or int* b = &a;
}
// error: invalid initialization of reference of type 'int&' from
// expression of type 'const int'
You can use const_cast to hack around the type safety if you really must, but recall that you're doing exactly that: getting rid of the type safety. It's still undefined to modify a through b in the below example:
int main() {
const int a = 3;
int& b = const_cast<int&>(a);
b = 3;
}
Although it compiles without errors, anything can happen including opening a black hole or transferring all your hard-earned savings into my bank account.
If you have arrived at what you think is a requirement to do this, I'd urgently revisit your design because something is very wrong with it.
Changing a constant type will lead to an Undefined Behavior.
However, if you have an originally non-const object which is pointed to by a pointer-to-const or referenced by a reference-to-const then you can use const_cast to get rid of that const-ness.
Casting away constness is considered evil and should not be avoided. You should consider changing the type of the pointers you use in vector to non-const if you want to modify the data through it.
The actual code to cast away the const-ness of your pointer would be:
BoxT<T> * nonConstObj = const_cast<BoxT<T> *>(constObj);
But note that this really is cheating. A better solution would either be to figure out why you want to modify a const object, and redesign your code so you don't have to.... or remove the const declaration from your vector, if it turns out you don't really want those items to be read-only after all.
Leaving this here for myself,
If I get this error, I probably used const char* when I should be using char* const.
This makes the pointer constant, and not the contents of the string.
const char* const makes it so the value and the pointer is constant also.
void SomeClass::changeASettingAndCallAFunction() const {
someSetting = 0; //Can't do this
someFunctionThatUsesTheSetting();
}
Another solution is to call said function in-between making edits to variables that the const function uses. This idea was what solved my problem being as I was not inclined to change the signature of the function and had to use the "changeASettingAndCallAFunction" method as a mediator:
When you call the function you can first make edits to the setting before the call, or (if you aren't inclined to mess with the invoking place) perhaps call the function where you need the change to the variable to be propagated (like in my case).
void SomeClass::someFunctionThatUsesTheSetting() const {
//We really don't want to touch this functions implementation
ClassUsesSetting* classUsesSetting = ClassUsesSetting::PropagateAcrossClass(someSetting);
/*
Do important stuff
*/
}
void SomeClass::changeASettingAndCallAFunction() const {
someFunctionThatUsesTheSetting();
/*
Have to do this
*/
}
void SomeClass::nonConstInvoker(){
someSetting = 0;
changeASettingAndCallAFunction();
}
Now, when some reference to "someFunctionThatUsesTheSetting" is invoked, it will invoke with the change to someSetting.

Const before or const after?

To start you probably know that const can be used to make either an object's data or a pointer not modifiable or both.
const Object* obj; // can't change data
Object* const obj; // can't change pointer
const Object* const obj; // can't change data or pointer
However you can also use the syntax:
Object const *obj; // same as const Object* obj;
The only thing that seems to matter is which side of the asterisk you put the const keyword. Personally I prefer to put const on the left of the type to specify it's data is not modifiable as I find it reads better in my left-to-right mindset but which syntax came first?
More importantly why is there two correct ways of specifying const data and in what situation would you prefer or need one over the other if any?
Edit:
So it sounds like this was an arbitrary decision when the standard for how compilers should interpret things was drafted long before I was born. Since const is applied to what is to the left of the keyword (by default?) I guess they figured there was no harm in adding "shortcuts" to apply keywords and type qualifiers in other ways at least until such a time as the declaration changes by parsing a * or & ...
This was the case in C as well then I'm assuming?
why is there two correct ways of specifying const data and in what situation would you prefer or need one over the other if any?
Essentially, the reason that the position of const within specifiers prior to an asterisk does not matter is that the C grammar was defined that way by Kernighan and Ritchie.
The reason they defined the grammar in this way was likely that their C compiler parsed input from left-to-right and finished processing each token as it consumed that. Consuming the * token changes the state of the current declaration to a pointer type. Encountering const after * means the const qualifier is applied to a pointer declaration; encountering it prior to the * means the qualifier is applied to the data pointed to.
Because the semantic meaning does not change if the const qualifier appears before or after the type specifiers, it is accepted either way.
A similar sort of case arises when declaring function pointers, where:
void * function1(void) declares a function which returns void *,
void (* function2)(void) declares a function pointer to a function which returns void.
Again the thing to notice is that the language syntax supports a left-to-right parser.
The rule is:
const applies to the thing left of it. If there is nothing on the left then it applies to the thing right of it.
I prefer using const on the right of the thing to be const just because it is the "original" way const is defined.
But I think this is a very subjective point of view.
I prefer the second syntax. It helps me keep track of 'what' is constant by reading the type declaration from right to left:
Object * const obj; // read right-to-left: const pointer to Object
Object const * obj; // read right-to-left: pointer to const Object
Object const * const obj; // read right-to-left: const pointer to const Object
The order of the keywords in a declaration isn't all that fixed. There are many alternatives to "the one true order". Like this
int long const long unsigned volatile i = 0;
or should it be
volatile unsigned long long int const i = 0;
??
The first rule is to use whichever format your local coding standards
requires. After that: putting the const in front leads to no end of
confusion when typedefs are involved, e.g.:
typedef int* IntPtr;
const IntPtr p1; // same as int* const p1;
If your coding standard allows typedef's of pointers, then it really
should insist on putting the const after the type. In every case but
when applied to the type, const must follow what it applies to, so
coherence also argues in favor of the const after. But local coding
guidelines trump all of these; the difference isn't normally important
enough to go back and change all of the existing code.
There are historical reasons that either left or right is acceptable. Stroustrup had added const to C++ by 1983, but it didn't make it to C until C89/C90.
In C++ there's a good reason to always use const on the right. You'll be consistent everywhere because const member functions must be declared this way:
int getInt() const;
C uses a right-to-left syntax. Just read the declarations from right to left:
int var = 0;
// one is a pointer to a const int
int const * one = &var;
// two is a pointer to an int const (same as "const int")
const int * two = &var;
// three is a constant pointer to an int
int * const three = &var;
The first thing left to the "const" is affected by it.
For more fun read this guide:
http://cseweb.ucsd.edu/~ricko/rt_lt.rule.html
With
using P_Int = int *;
P_Int const a = nullptr;
int * const b = nullptr;
const P_Int c = nullptr;
const int * d = nullptr;
the variables a and b are the same type as each other but, somewhat confusingly, the variables c and d are not the same type as each other. My preference is for the first scenario, without the confusion: have const on the right of the type. N.B. it is the pointer that is const with a, b, and c; but the int is const with d.
When I work with the existing code, I follow the way already being used which is most of the time, const on left e.g const int a = 10; but if I got a chance to work from start I choose the "right way", means const on right e.g int const a = 10;, which is a more universal approach and straightforward to read.
As mentioned already, the rule is
const applies to the thing left of it. If there is nothing on the left then it applies to the thing right of it.
Here is the example code for basic use cases.
int main() {
int const a = 10; // Constant integer
const int b = 20; // Constant integer (same as above)
int c = 30; // Integer (changeable)
int * const d = &c; // Constant pointer to changeable integer
int const * e = &a; // Changeable pointer to constant integer
int const * const f = &a; // Constant pointer to constant integer
return 0;
}
At the end of the day, if there is no guideline to follow, this is subjective, and harmless to use either approach.