Why does "most important const" have to be const? - c++

In http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/ it mentions "most important const" where by C++ deliberately specifies that binding a temporary object to a reference to const on the stack lengthens the lifetime of the temporary to the lifetime of the reference itself. I was wondering why c++ only allows the lifetime of the object to be lengthened when the reference is const and not when it isn't? What is the rational behind the feature and why does it have to be const?

Here's an example:
void square(int &x)
{
x = x * x;
}
int main()
{
float f = 3.0f;
square(f);
std::cout << f << '\n';
}
If temporaries could bind to non-const lvalue references, the above would happily compile, but produce rather surprising results (an output of 3 instead of 9).

Consider the following:
int& x = 5;
x = 6;
What should happen if this was allowed? By contrast, if you did
const int& x = 5;
there would be no legal way to modify x.

Note that const references can be bound to objects that don't even have an address normally. A const int & function parameter can take an argument formed by the literal constant expression 42. We cannot take the address of 42, so we cannot pass it to a function that takes a const int *.
const references are specially "blessed" to be able to bind to rvalues such as this.
Of course, for traditional rvalues like 2 + 2, lifetime isn't an issue. It's an issue for rvalues of class type.
If the binding of a reference is allowed to some object which, unlike 42, does not have a pervasive lifetime, that lifetime has to be extended, so that the reference remains sane throughout its scope.
It's not that the const causes a lifetime extension, it's that a non-const reference is not allowed. If that were allowed, it would also require a lifetime extension; there is no point in allowing some reference which then goes bad in some parts of its scope. That behavior undermines the concept that a reference is safer than a pointer.

Related

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

C++ Difference Between Const Reference to Non Const Object and Non Const Reference to Non Const Object

What is the purpose for the "const" keyword for a reference if the object it is referencing is not a const object? Is there any difference between what r1 and r2 does (below)?
int i = 42; // non const object
const int &r1 = i; // const reference to non const object
int j = 25; // non const object
int &r2 = j; // non const reference to non const object
Here's a quote from CPP Primer 5th:
"C++ programmers tend to abbreviate the phrase “reference to const” as
“const reference.” This abbreviation makes sense—if you remember that it
is an abbreviation.
Technically speaking, there are no const references. A reference is not an
object, so we cannot make a reference itself const. Indeed, because there
is no way to make a reference refer to a different object, in some sense all
references are const. Whether a reference refers to a const or nonconst
type affects what we can do with that reference, not whether we can alter
the binding of the reference itself."
I think this means that making a reference a "const" when it is referenced to a non const object does absolutely nothing. We may as well take that const keyword out when defining that reference.
Asking this question here for confirmation.
Edit: Looks like my initial conjecture is wrong. I understand now that a const reference to a non const object does have a purpose: to prevent the reference from modifying the object. The non const object can still be modified by other means but not by this const reference.
Thanks all.
"What is the purpose for the "const" keyword for a reference if the object it is referencing is not a const object?"
The purpose is to prevent that reference being used to modify the object it is referencing.
int i = 42; // non const object
const int &r1 = i; // const reference to non const object
r1 = 6 * 9; // error, r1 cannot be used to modify i;
To understand it better you can look into difference between const pointer and pointer to a const data:
int i, j;
const int *p1 = &i; // pointer to constant int
int *const p2 = &i; // constant pointer to int
*p1 = 0; // error, p1 points to const int
*p2 = 0; // it is fine sets i to 0
p1 = &j; // fine p1 now points to anbother int
p2 = &j; // error, p2 is a constant pointer
so now if we replace pointer to reference we can see similar things, except reference by itself is not changeable ie you cannot make reference to refer to another object after it is created (unlike non constant pointer) and any reference is like constant pointer. So const reference in this meaning does not make any sense and usually by const reference people mean reference to a const type. That what quote from primer means.
As for difference in your code, yes there is difference - you cannot change object through const reference does not matter if that reference points to const object or not.
There's a useful way to figure out what constness means in pointers and references, which is to read the declaration from right to left (see these answers). So const int &r1 can be read as "r1 is a reference to an int const".
Basically, r1 refers to an int which cannot be modified. This means that the referred-to int is either a const int, or it's a simple int. The binding of a const const reference to a non-const object (for example, when writing something like int i = 5; const int& r = i;) is perfectly legal because there's nothing wrong with not modifying a non-const object.
I think this means that making a reference a "const" when it is referenced to a non const object does absolutely nothing. We may as well take that const keyword out when defining that reference.
Not true.
You may not modify the a non-const object through a const reference.
You may modify a non-const object through a non-const reference.
Unless an object is created in the read-only section of a program, it is open for modifiction without adverse consequences. However, when you use a const reference to a non-const object, you are asking the compiler to not let you modify the object through that particular reference. It does not mean that you will not modify object.
It's similar to function arguments. When a function uses a const reference argument type, the function is promising to you that it will not modify the object. It does not mean that the object is not modifiable at all.
If we use const with variable then its value can not be change and when it is used with const reference then its reference can not be changed if we used it with object then whole data is used in object it can not be changed.
It's also worth to mention the behavior when you pass both to a function call
fun1(const int& R1) vs. fun2(int & R1)
In fun1 you may call it with either cost or variable, for example fun1(5) or fun1(var1); assuming int var1=5;
In fun2, you simply can't call it with const, fun2(5) will give you compiler error, which means in that case you've to add another overloading implementation to cover this case. Bad design!

The usage of const reference in C++

I have a question about reference in C++.
this line int& refVal4 = 10; is wrong,
but const int& refVal4 = 10; is right.
So i am confused, who can help me clear this problem?
In your code, int & is a non-const lvalue reference. const int & is a const lvalue reference.
The rules about reference binding are that a non-const lvalue reference may only bind to an lvalue expression. If you are unsure what an lvalue expression is, see this answer. 10 is a prvalue expression.
This rule does not reflect some underlying truth about the nature of the number ten or anything, as suggested in many comments. Rather, it is an arbitrary decision. To avoid the red herring of 10 (which is a non-const int, by the way), we could instead consider;
int & y1 = atoi(6); // error
const int & y2 = atoi(6); // OK
int && y3 = atoi(6); // OK
++y3; // OK
In the cases y2 and y3 , the rules of reference binding are that a temporary object is created which is initialized from the value in the initializer. Then the reference binds to that temporary object (and the lifetime of that temporary object is extended to match the lifetime of the reference).
It is the same for binding to 10 : we do not bind to some platonic ideal or some fixed memory location that is preset to contain 10. In fact we create a temporary int whose value is 10, and bind to that. It is nonsense to talk about "changing the value of 10".
As you can see from the y3 example, there is no technical reason why y1 could not work. In fact, disallowing y1 was a design decision made by Bjarne Stroustrup. He felt that allowing this would lead to coders making more mistakes. For example:
void increment(int &x) { ++x; }
int main()
{
long y = 123;
increment(y);
}
If the non-const lvalue binding were allowed then this code would compile successfully and have no effect on y. x would bind to a temporary int. Bjarne felt it would be better if this code gave a compilation error because the programmer usually would not want this to happen.
int& refVal4 = 10; Here refVal4 is a reference to a non-const. Hence, the user can do something like refVal4 = 20. Now, if refVal4 is pointing to a r-value (10) then the refVal = 20; (which is same as saying 10=20) does not make sense and is invalid. However, const int& refVal4 = 10; is perfectly valid as it is a reference to a constant and hence one cannot modify by saying refVal = 20; and results in compile time error. So, the compiler allows a reference to const to refer to r-values (and l-values) as the user is only allowed to read - (int val = refVal4) and not change the referenced value.

error: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’

Wrong form:
int &z = 12;
Correct form:
int y;
int &r = y;
Question:
Why is the first code wrong? What is the "meaning" of the error in the title?
C++03 3.10/1 says: "Every expression is either an lvalue or an rvalue." It's important to remember that lvalueness versus rvalueness is a property of expressions, not of objects.
Lvalues name objects that persist beyond a single expression. For example, obj , *ptr , ptr[index] , and ++x are all lvalues.
Rvalues are temporaries that evaporate at the end of the full-expression in which they live ("at the semicolon"). For example, 1729 , x + y , std::string("meow") , and x++ are all rvalues.
The address-of operator requires that its "operand shall be an lvalue". if we could take the address of one expression, the expression is an lvalue, otherwise it's an rvalue.
&obj; // valid
&12; //invalid
int &z = 12;
On the right hand side, a temporary object of type int is created from the integral literal 12, but the temporary cannot be bound to non-const reference. Hence the error. It is same as:
int &z = int(12); //still same error
Why a temporary gets created? Because a reference has to refer to an object in the memory, and for an object to exist, it has to be created first. Since the object is unnamed, it is a temporary object. It has no name. From this explanation, it became pretty much clear why the second case is fine.
A temporary object can be bound to const reference, which means, you can do this:
const int &z = 12; //ok
C++11 and Rvalue Reference:
For the sake of the completeness, I would like to add that C++11 has introduced rvalue-reference, which can bind to temporary object. So in C++11, you can write this:
int && z = 12; //C+11 only
Note that there is && intead of &. Also note that const is not needed anymore, even though the object which z binds to is a temporary object created out of integral-literal 12.
Since C++11 has introduced rvalue-reference, int& is now henceforth called lvalue-reference.
12 is a compile-time constant which can not be changed unlike the data referenced by int&. What you can do is
const int& z = 12;
Non-const and const reference binding follow different rules
These are the rules of the C++ language:
an expression consisting of a literal number (12) is a "rvalue"
it is not permitted to create a non-const reference with a rvalue: int &ri = 12; is ill-formed
it is permitted to create a const reference with a rvalue: in this case, an unnamed object is created by the compiler; this object will persist as long as the reference itself exist.
You have to understand that these are C++ rules. They just are.
It is easy to invent a different language, say C++', with slightly different rules. In C++', it would be permitted to create a non-const reference with a rvalue. There is nothing inconsistent or impossible here.
But it would allow some risky code where the programmer might not get what he intended, and C++ designers rightly decided to avoid that risk.
References are "hidden pointers" (non-null) to things which can change (lvalues). You cannot define them to a constant. It should be a "variable" thing.
EDIT::
I am thinking of
int &x = y;
as almost equivalent of
int* __px = &y;
#define x (*__px)
where __px is a fresh name, and the #define x works only inside the block containing the declaration of x reference.

Do rvalue references allow dangling references?

Consider the below.
#include <string>
using std::string;
string middle_name () {
return "Jaan";
}
int main ()
{
string&& danger = middle_name(); // ?!
return 0;
}
This doesn't compute anything, but it compiles without error and demonstrates something that I find confusing: danger is a dangling reference, isn't it?
Do rvalue references allow dangling references?
If you meant "Is it possible to create dangling rvalue references" then the answer is yes. Your example, however,
string middle_name () {
return "Jaan";
}
int main()
{
string&& nodanger = middle_name(); // OK.
// The life-time of the temporary is extended
// to the life-time of the reference.
return 0;
}
is perfectly fine. The same rule applies here that makes this example (article by Herb Sutter) safe as well. If you initialize a reference with a pure rvalue, the life-time of the tempoary object gets extended to the life-time of the reference. You can still produce dangling references, though. For example, this is not safe anymore:
int main()
{
string&& danger = std::move(middle_name()); // dangling reference !
return 0;
}
Because std::move returns a string&& (which is not a pure rvalue) the rule that extends the temporary's life-time doesn't apply. Here, std::move returns a so-called xvalue. An xvalue is just an unnamed rvalue reference. As such it could refer to anything and it is basically impossible to guess what a returned reference refers to without looking at the function's implementation.
rvalue references bind to rvalues. An rvalue is either a prvalue or an xvalue [explanation]. Binding to the former never creates a dangling reference, binding to the latter might. That's why it's generally a bad idea to choose T&& as the return type of a function. std::move is an exception to this rule.
T& lvalue();
T prvalue();
T&& xvalue();
T&& does_not_compile = lvalue();
T&& well_behaved = prvalue();
T&& problematic = xvalue();
danger is a dangling reference, isn't it?
Not any more than if you had used a const &: danger takes ownership of the rvalue.
Of course, an rvalue reference is still a reference so it can be dangling as well. You just have to bring the compiler into a situation where he has to drag the reference along and at the same time you just escape the refered-to value's scope, like this:
Demo
#include <cstdio>
#include <tuple>
std::tuple<int&&> mytuple{ 2 };
auto pollute_stack()
{
printf("Dumdudelei!\n");
}
int main()
{
{
int a = 5;
mytuple = std::forward_as_tuple<int&&>(std::move(a));
}
pollute_stack();
int b = std::get<int&&>(mytuple);
printf("Hello b = %d!\n", b);
}
Output:
Dumdudelei!
Hello b = 0!
As you can see, b now has the wrong value. How come? We stuffed an rvalue reference to an automatic variable a into a global tuple. Then we escaped the scope of a and retrieve its value through std::get<int&&> which will evaluate to an rvalue-reference. So the new object b is actually move constructed from a, but the compiler doesn't find a because its scope has ended already. Therefore std::get<int&&> evaluates to 0 (although it is probably UB and could evaluate to anything).
Note that if we don't touch the stack, the rvalue reference will actually still find the original value of object a even after its scope has ended and will retrieve the right value (just try it and uncomment pollute_stack() and see what happens). The pollute_stack() function just moves the stack pointer forward and back while writing values to the stack by doing some io-related stuff through printf().
The compiler doesn't see through this though at all so be aware of this.