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.
Related
If we want to initialize an reference with an different type, we need to make it const (const type*) so that an temporary can be generated implicit and the reference binded to with. Alternativaly, we can use r-value references and achieve the same [1]:
Rvalue references can be used to extend the lifetimes of temporary objects (note, lvalue references to const can extend the lifetimes of temporary objects too, but they are not modifiable through them) :
[...]
Samples
Case 1
double x = 10;
int &ref = x; //compiler error (expected)
Case 2
double x = 10;
const int &ref = x; //ok
Case 3
double x = 10;
int &&ref = x; //ok
If we try to do the same with reference to const pointer (const type* &) and initialize it with non-const pointer (type*), different than I expected, only the case 2 works. Why the case 3 leads to compiler error? Why the temporary isn't generated?
Case 1
int x = 10;
int *pX = &x;
const int* &ref = pX; //compiler error (expected)
Case 2
int x = 10;
int *pX = &x;
const int* const &ref = pX; //ok (expected)
Case 3
int x = 10;
int *pX = &x;
const int* &&ref = pX; //compiler error (why?)
In gcc 12.1.0 and clang 14.0.4 with flag -std=c++20 (and some others) the case 3 above don't compile.
gcc : 'error: cannot bind rvalue reference of type 'const int*&&' to lvalue of type 'int*''
clang: 'error: rvalue reference to type 'const int *' cannot bind to lvalue of type 'int *'
Why in the case of int&, int&&, etc., all worked well, and in this case with pointer there was compiler error? Is there some imprecision in my current knowledge? (I'm novice)
If we do the same with an pr-value (int*), everything works well
Case 3
int x = 10;
//int *pX = &x;
const int* &&ref = &x; //ok (why?)
Related questions:
<Non-const reference to a non-const pointer pointing to the const object>
<const reference to a pointer not behaving as expected>
Similar questions but both suggest using reference to const (type* const &). I wonder why r-value reference dont works with pointer, but work with int, etc., and that wasn't asked.
<What does T&& (double ampersand) mean in C++11?>
r-value reference (&&)
References
[1] https://en.cppreference.com/w/cpp/language/reference
The standard has a concept of two types being reference-related. This is fulfilled by two types being similar, which basically means if they are the same type with the same number of pointers but possibly different cv-qualifiers (e.g., int and const int are similar, int* const ** volatile and volatile int** const * are similar, and crucially int* and const int* are similar).
The standard says that ([dcl.init.ref]p(5.4.4)):
If T1 is reference-related to T2:
if the reference is an rvalue reference, the initializer expression shall not be an lvalue.
And since const int* &&ref = pX;, an rvalue reference, and T1 = const int* is reference-related to T2 = decltype(pX) = int*, this applies and so this just isn't allowed. const int* &&ref = std::move(pX); doesn't run into this issue, since the initializer is no longer an lvalue. And of course, explicitly doing the const conversion const int* &&ref = (const int*) pX; also works.
Presumably, this is so const T x; T&& y = x; (binding y to a temporary copy of x) isn't allowed, but by a quirk of the standard it also extends to pointers.
As you've noted, when an lvalue is used in a context that requires a different type, C-family languages will convert the referenced value to a temporary rvalue of the required type if that's possible.
Also as noted, in the specific case of reference initialization, rvalue temporaries can be given longer lives; the compiler has to have some value to refer to, what possible argument could there be for not accepting this temporary value you just constructed? (Let's just ignore "desperately confusing the uninitiated?").
double x = 10;
const int &ref = x; //ok // <== editorial comment: legal, but no, not ok
That's not strictly illegal, and to make it illegal there'd have to be a special rule to distinguish it from initialization of that same ref declaration as a function argument. The open-code case doesn't lead to any damage either, it's not worth writing a specific rule to forbid, because why would anyone ever do that? And if they try, well, can we call it a teachable moment?
int x = 10;
int *pX = &x;
const int* &&ref = pX; //compiler error (why?)
This is not the same as the case above. You specifically declared a reference to a temporary, and there's no temporary.
Reference syntax was invented to make pass-by-reference work. A constant lvalue reference to a temporary doesn't lead to trouble, a non-constant reference to a temporary can: the receiver might be treating it as an out-parameter, and the caller might not notice the conversion that means a temporary is being passed.
rvalue references are sausage-making devices added later after nobody could find a better plan. They're specifically for values known to be temporaries, among other reasons so the receiver can be certain of ownership. They're not ruled out in open code same as before, but outside the parameter-passing context they look as bafflingly out of place and behave as strangely as a tunafish flopping in your driveway.
Don't, just don't, try to declare rvalue references in open code. That is a rabbit hole desperate criminals like library implementors must descend, because they've been promised freedom for themselves and their families if their mission succeeds. Rvalue references may not be initialized from lvalue references because if they were, it would break function call overloads on temporaries.
The reason why
int x = 10;
int *pX = &x;
const int* &&ref = pX; //compiler error (why?)
doesn't compile is for the same reason as why
int x = 10;
int&& y = x; // doesn't compile
const int&& z =x; // doesn't compile
That's cause you try to assign an l-value reference to an r-value reference. That's straight up forbidden.
The reason why
double x = 10;
int&& z = x;
compiles is because here one doesn't try to assign l-value reference to an r-value reference; this part is inherently impossible so compiler first converts double to int and now your equation becomes binding an r-value reference to a temporary which totally makes sense for compiler.
I am sure you can figure out now why Case 3 compiles
int x = 10;
const int* &&ref = &x;
that's cause &x is a temporary and can be bound to an r-value reference.
Generally speaking, this approach is ill advised as it is confusing, misleading, and can lead to a ton of bugs in the code. Consider asking a question about better design. It might be a question towards codereview or some other site.
Note: please don't confuse it with auto&& x = y; That's something different and pretty much always compiles.
There is a big difference between:
const int *x
which is a pointer to a constant int, and a
int * const x
which is a constant pointer to an int. This is something completely different.
For starters, a pointer to an int doesn't have many implicit conversions, so it's not like it can be implicitly converted to some other, different, kind of a pointer.
But, if you want to implictly convert the pointer to a constant pointer, sure:
int x = 10;
int *pX = &x;
int * const &&ref = std::move(pX);
Note: std::move must be used for the same reason why the following doesn't work:
int x=10;
int &&y=x;
I'm currently reading c++ Primer and in one of the exercises, it shows 2 types of reference initializations, one const and and one not, and in the one const, the reference points to 0.. I thought references could only point to objects!!
int i = -1, &r = 0; // illegal, r must refer to an object.
const int i = -1, &r = 0; // legal.
So the first one, int variable i is -1. Then int reference r refers to 0 which is
invalid,gotcha
Now in the second one, const int i = -1, then const reference r = 0.. How?
I know references can't be const, the const is what the reference is referring to, and the book said that const references can refer to both const and non const objects(of the same type). This still doesn't explain why it can point to 0, which is not an object at all..
I'm not sure how the second statement is valid, someone please explain!
In
const int &r = 0;
a temporary object is created with the value 0, and then r is bound to that object. This is allowed because a const reference is allowed to bind to an rvalue. It also works with an rvalue reference:
int&& r = 0; // ok
However, it won't work with int& because an int& variable can only bind to an lvalue.
Note that the temporary object that the reference binds to will live until the reference goes out of scope. See https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
C++ specifically allows that an rvalue be bound to a const lvalue reference, so this is perfectly standard-conforming:
const int &r = 0;
Reference
An rvalue may be used to initialize a const lvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
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.
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.
Why do constant references not behave the same way as constant pointers, so that I can actually change the object they are pointing to? They really seem like another plain variable declaration. Why would I ever use them?
This is a short example that I run which compiles and runs with no errors:
int main (){
int i=0;
int y=1;
int&const icr=i;
icr=y; // Can change the object it is pointing to so it's not like a const pointer...
icr=99; // Can assign another value but the value is not assigned to y...
int x=9;
icr=x;
cout<<"icr: "<<icr<<", y:"<<y<<endl;
}
The clearest answer.
Does “X& const x” make any sense?
No, it is nonsense
To find out what the above declaration means, read it right-to-left:
“x is a const reference to a X”. But that is redundant — references
are always const, in the sense that you can never reseat a reference
to make it refer to a different object. Never. With or without the
const.
In other words, “X& const x” is functionally equivalent to “X& x”.
Since you’re gaining nothing by adding the const after the &, you
shouldn’t add it: it will confuse people — the const will make some
people think that the X is const, as if you had said “const X& x”.
The statement icr=y; does not make the reference refer to y; it assigns the value of y to the variable that icr refers to, i.
References are inherently const, that is you can't change what they refer to. There are 'const references' which are really 'references to const', that is you can't change the value of the object they refer to. They are declared const int& or int const& rather than int& const though.
What is a constant reference (not a reference to a constant)
A Constant Reference is actually a Reference to a Constant.
A constant reference/ Reference to a constant is denoted by:
int const &i = j; //or Alternatively
const int &i = j;
i = 1; //Compilation Error
It basically means, you cannot modify the value of type object to which the Reference Refers.
For Example:
Trying to modify value(assign 1) of variable j through const reference, i will results in error:
assignment of read-only reference ‘i’
icr=y; // Can change the object it is pointing to so it's not like a const pointer...
icr=99;
Doesn't change the reference, it assigns the value of the type to which the reference refers.
References cannot be made to refer any other variable than the one they are bound to at Initialization.
First statement assigns the value y to i
Second statement assigns the value 99 to i
This code is ill-formed:
int&const icr=i;
Reference: C++17 [dcl.ref]/1:
Cv-qualified references are ill-formed except when the cv-qualifiers are introduced
through the use of a typedef-name or decltype-specifier, in which case the cv-qualifiers are ignored.
This rule has been present in all standardized versions of C++. Because the code is ill-formed:
you should not use it, and
there is no associated behaviour.
The compiler should reject the program; and if it doesn't, the executable's behaviour is completely undefined.
NB: Not sure how none of the other answers mentioned this yet... nobody's got access to a compiler?
By "constant reference" I am guessing you really mean "reference to constant data". Pointers on the other hand, can be a constant pointer (the pointer itself is constant, not the data it points to), a pointer to constant data, or both.
As it mentioned in another answers, a reference is inherently const.
int &ref = obj;
Once you initialized a reference with an object, you can't unbound this reference with its object it refers to. A reference works just like an alias.
When you declare a const reference, it is nothing but a reference which refers to a const object.
const int &ref = obj;
The declarative sentences above like const and int is determining the available features of the object which will be referenced by the reference. To be more clear, I want to show you the pointer equivalent of a const reference;
const int *const ptr = &obj;
So the above line of code is equivalent to a const reference in its working way. Additionally, there is a one last point which I want to mention;
A reference must be initialized only with an object
So when you do this, you are going to get an error;
int &r = 0; // Error: a nonconst reference cannot be initialized to a literal
This rule has one exception. If the reference is declared as const, then you can initialize it with literals as well;
const int &r = 0; // a valid approach
First I think int&const icr=i; is just int& icr = i, Modifier 'const' makes no sense(It just means you cannot make the reference refer to other variable).
const int x = 10;
// int& const y = x; // Compiler error here
Second, constant reference just means you cannot change the value of variable through reference.
const int x = 10;
const int& y = x;
//y = 20; // Compiler error here
Third, Constant references can bind right-value. Compiler will create a temp variable to bind the reference.
float x = 10;
const int& y = x;
const int& z = y + 10;
cout << (long long)&x << endl; //print 348791766212
cout << (long long)&y << endl; //print 348791766276
cout << (long long)&z << endl; //print 348791766340