int main(){
int x = 10;
const int&z = x;
int &y = z; // why is this ill formed?
}
Why is initializing non constant reference to int to a constant reference not correct? What is the reason behind this?
Well, why shouldn't it be ill-formed?
It is ill-formed because it breaks the obvious rules of const correctenss. In C++ language you are not allowed to implicitly convert the constant access pass to a non-constant access path. It is the same for pointers and for references. That's the whole purpose of having constant access paths: to prevent modification of the object the path leads to. Once you made it constant, you are not allowed to go back to non-constant, unless you make a specific explicit and conscious effort to do that by using const_cast.
In this particular case you can easily remove the constness from the access path by using const_cast (this is what const_cast is for) and legally modify the referenced object, since the referenced object is not really constant
int main(){
int x = 10;
const int &z = x;
int &y = const_cast<int &>(z);
y = 42; // modifies x
}
Since y isn't const, you would be able to write y = 42 and change z (which is const) too.
Because a const reference is unmodifiable, while a standard reference is.
The compiler assumes that the thing referred to by a const int & is a const int, even though in this case it isn't. You cannot make a non-const reference refer to a const int, because you would then be able to change the (notionally) const int via the reference.
Because
int const x = 10;
int const& z = x;
int& y = z;
y = 42;
would modify a constant variable.
As others say, it would allow one to indirectly change x, which breaks the const-correctness promise. See http://en.wikipedia.org/wiki/Const_correctness
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;
The following code compiles fine. However I wonder if it is legal C++. So more specific, if I have a const object, am I allowed to modify variables through pointers/references of that object?
class Foo {
public:
int* a;
int& b;
Foo(int* _a, int& _b) : a(_a), b(_b) {}
};
int main ( int argc, char* argv[] ) {
int x = 7;
const Foo bar(&x, x);
*bar.a = 3; //Leagal?
bar.b = 7; //Legal?
return 0;
}
It's legal, as const-ness of the class means that the class member is constant. a is a pointer, so the address the pointer points to is constant, but the value stored at that address need not be.
Hence bar.a is effectively an int * const, not an int const *.
As, after initialization, a reference cannot be made to refer to another entity anyway, it does not matter for bar.b whether bar is declared const or not.
The constant variant of a pointer is a constant pointer, not a pointer to a constant. The constant variant of a reference is a reference, not a reference to a constant.
Small digression: You should be careful with references as members anyway in connection with const-ness, as the following will probably compile
struct Y { int m_a; };
struct X {
const Y & m_y;
X (const Y & y) : m_y (y) { }
};
Y y;
y.m_a = 1;
X x (y); // or const X x (y) -- does not matter
// X.m_y.m_a == 1
y.m_a = 2;
// now X.m_y.m_a == 2, although X.m_y is supposed to be const
As it is possible to assign a pointer to non-const to a pointer to const, you can build an analogous example with pointers. Remember that const does only guarantee that YOU will not modify a variable via this very variable, it cannot guarantee that the contents of the variable are not modified at all.
I find the best way to think about const in C++ is that it protects the physical bits of the object. It has no real protection for objects that it refers to. You can control the object definition with const methods to provide deeper protection of values but by default C++ really only protects the physical object itself
One of the reasons why C++ allows you to modify the contents of a pointer through a const value is that it really can't stop you. Imagine for a second that C++ disallowed that particular construct. You could trivially violate it by doing the following
size_t t1 = (size_t)bar.a; // legal
int* t2 = (int*)t1; // legal
*t2 = 3; // exactly the same as *bar.a = 3
I am reading about references in C++. It says that int& a = 5 gives compile time error.
In Thinking in C++ - Bruce Eckel, author says that compiler must first allocate the storage for an int and produce the address to bind to the reference. The storage must be const because changing it would make no sense.
I am confused at this point. I am not able to understand the logic behind it. Why can't be change the content in the storage? I understand that it's invalid as per C++ rules, but why?
"The storage must be const because changing it would make no sense."
If you want a be a reference to a const value, you must declare it as const, because a is referencing to a temporary constant value, and changing it is not possible.
const int &a = 123;
a = 1000; // `a` is referencing to temporary 123, it is not possible to change it
// We can not change 123 to 1000
// Infact, we can change a variable which its value is 123 to 1000
// Here `a` is not a normal variable, it's a reference to a const
// Generally, `int &a` can not bind to a temporary object
For non-const bindings:
int x = 1;
int &a = x;
a is a reference to a lvalue. Simple speaking, it's an alias name for another variable, so on the right hand you should give a variable. The reference a can not change and bind to another variable after it's first binding;
In C++11, you can reference to temporary objects/values by rvalue references:
int &&a = 123;
int& a = 5;
In order for the above code to work, int& needs to bind to a temporary object of type int created out of the expression 5. But binding int& to a temporay didn't appeal to Bjarne Stroustrup — and he gave an example, similar to the following, to illustrate his point:
void f(int &i) { ++i; }
float x = 10.0;
f(x);
std::cout << x <<< std::endl;
What will the std::cout print1? Looks like it will print 11.
It feels, ++i is changing the argument x, but it doesn't. This is one reason why the creator of C++ didn't permit temporaries to bind to non-const reference.
However, you can do this:
int const & i = 10;
int const & j = x; //x is float
And since C++11, you can do this:
int && i = 10;
int && i = x; //x is float
Hope that helps.
1. assuming int& can bind to the temporary created out of x.
What you can do is
int b=5;
int &a=b;
or
const int& a = 5;
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
In the following, would there be a temporary object created before const reference is used to a non-const object?
const int y = 2000;
const int &s = y // ok, const reference to const object.
int x = 1000;
const int &r = x; // any temporary copy here?
If no then, how does this work?
const int z = 3000;
int &t = z // ok, why can't you do this?
No.
A reference is simply an alias for an existing object. const is enforced by the compiler; it simply checks that you don't attempt to modify the object through the reference r.* This doesn't require a copy to be created.
Given that const is merely an instruction to the compiler to enforce "read-only", then it should be immediately obvious why your final example doesn't compile. const would be pointless if you could trivially circumvent it by taking a non-const ref to a const object.
* Of course, you are still free to modify the object via x. Any changes will also be visible via r, because they refer to the same object.
In
int x = 1000;
const int &r = x;
the right-hand side is an lvalue and the type of x is the same as the type of the reference (ignoring cv-qualifications). Under these circumstances the reference is attached directly to x, no temporary is created.
As for "how does this work"... I don't understand what prompted your question. It just works in the most straighforward way: the reference is attached directly to x. Nothing more to it.
You can't do
const int z = 3000;
int &t = z;
because it immediately violates the rules of const-correctness.
The understanding on the Reference (&) answers this question..
Reference is just an alias to the variable that it is assigned to it..
And const is a constraint imposed by the compiler to the variable that is declared as const
int x = 1000;
const int &r = x;
In this case, its a const reference to a non const variable. So you cannot change the data of x with reference variable r(just acts a read only).. yet you can still change the data x by modifying x
const int z = 3000;
int &t = z
In this case, non const reference to const member which is meaningless. You are saying reference can allow you to edit a const member(which is never possible)..
So if you want to create a reference for a const member, it has to be like the first case you mentioned
const int z = 3000;
const int &t = z;