#include <iostream>
using namespace std;
int main()
{
int i = 0;
cout << &i << endl;
const auto &ref = (short&&)i;
cout << &ref << endl;
return 0;
}
Why is &i different from &ref? (short&)i doesn't cause this problem. Does (short&&)i generate a temporary variable?
It's because you're doing a different type of cast. The C style explicit conversion cast does always a static cast, if it could be interpreted as a static cast; otherwise it does a reinterpret cast. And/or const cast as needed.
(short&&)i is a static cast because it can be interpreted as static_cast<short&&>(i). It creates a temporary short object, to which ref is bound. Being a different object, it has a different address.
(short&)i is a reinterpret cast because it cannot be interpreted as static_cast<short&>(i) which is ill formed. It reinterprets the int reference as short reference, and ref is bound to the the same object. Note that accessing the object through this reference would have undefined behaviour.
This creates a lvalue reference to a thing that exists:
const auto& ref = i;
The expressions &ref and &i will therefore give the same result.
This is also true of:
const auto& ref = (int&)i;
which is basically the same thing.
However, casting to something that is not a lvalue reference to T (so, to a value, or to an rvalue reference of another type!) must create a temporary; this temporary undergoes lifetime extension when bound to ref. But now ref does not "refer to" i, so the address-of results will differ.
It's actually a little more complicated than that, but you get the idea. Besides, don't write code like this! An int is not a short and you can't pretend that it is.
Apparently it creates a temporary.
Actually the compiler will tell you itself.
Try this:
auto &ref = (short&&)i;
cout << &ref << endl;
The error says:
error: non-const lvalue reference to type 'short' cannot bind to a
temporary of type 'short'
Test code here.
(short&&)i creates a temporary, so you take address of an other object, so address might differ.
Related
Can someone tell me if this is safe, because I think it isn't:
class A
{
public:
A(int*& i) : m_i(i)
{}
int*& m_i;
};
class B
{
public:
B(int* const& i) : m_i(i)
{}
int* const & m_i;
};
int main()
{
int i = 1;
int *j = &i;
A a1(j); // this works (1)
A a2(&i); // compiler error (2)
B b(&i); // this works again (3)
}
I understand why (1) works. We are passing a pointer, the function accepts it as a reference.
But why doesn't (2) work? From my perspective, we are passing the same pointer, just without assigning it to a pointer variable first. My guess is that &i is an rvalue and has no memory of its own, so the reference cannot be valid. I can accept that explanation (if it's true).
But why the heck does (3) compile? Wouldn't that mean that we allow the invalid reference so b.m_i is essentially undefined?
Am I completely wrong in how this works? I am asking because I am getting weird unit test fails that I can only explain by pointers becoming invalid. They only happen for some compilers, so I was assuming this must be something outside the standard.
So my core question basically is: Is using int* const & in a function argument inherently dangerous and should be avoided, since an unsuspecting caller might always call it with &i like with a regular pointer argument?
Addendum: As #franji1 pointed out, the following is an interesting thought to understand what happens here. I modified main() to change the inner pointer and then print the members m_i:
int main()
{
int i = 1;
int *j = &i; // j points to 1
A a1(j);
B b(&i);
int re = 2;
j = &re; // j now points to 2
std::cout << *a1.m_i << "\n"; // output: 2
std::cout << *b.m_i << "\n"; // output: 1
}
So, clearly a1 works as intended.
However, since b cannot know that j has been modified, it seems to hold a reference to a "personal" pointer, but my worry is that it is not well defined in the standard, so there might be compilers for which this "personal" pointer is undefined. Can anyone confirm this?
A's constructor takes a non-const reference to an int* pointer. A a1(j); works, because j is an int* variable, so the reference is satisfied. And j outlives a1, so the A::m_i member is safe to use for the lifetime of a1.
A a2(&i); fails to compile, because although &i is an int*, operator& returns a temporary value, which cannot be bound to a non-const reference.
B b(&i); compiles, because B's constructor takes a reference to a const int*, which can be bound to a temporary. The temporary's lifetime will be extended by being bound to the constructor's i parameter, but will then expire once the constructor exits, thus the B::m_i member will be a dangling reference and not be safe to use at all after the constructor has exited.
j is an lvalue and as such it can be bound to a non-const lvaue reference.
&i is a prvalue and it cannot be bound to non-const lvalue reference. That's why (2) doesn't compile
&i is a prvalue (a temporary) and it can be bound to a const lvalue reference. Bounding a prvalue to a reference extends the lifetime of the temporary to the lifetime of the reference. In this case this temporary lifetime is extended to the lifetime of the constructor parameter i. You then initialize the reference m_i to i (constructor parameter) (which is a reference to the temporary) but because i is an lvalue the lifetime of the temporary is not extended. In the end you end up with a reference member m_i bound to an object which is not alive. You have a dangling reference. Accessing m_i from now on (after the constructor has finished) is Undefined Behavior.
Simple table of what can references bind to: C++11 rvalue reference vs const reference
Pointer is a memory address. For simplicity, think of a pointer as uint64_t variable holding a number representing the memory address of whatever. Reference is just a alias for some variable.
In example (1) you are passing a pointer to constructor expecting a reference to pointer. It works as intended, as compiler gets the address of memory where the value of pointer is stored and passes it to constructor. The constructor gets that number and creates an alias pointer. As a result you are getting an alias of j. If you modify j to point to something else then m_i will also be modified. You can modify m_i to point to something else too.
In example (2) you are passing a number value to the constructor expecting a reference to pointer. So, instead of an address of an address, constructor gets an address and compiler has no way to satisfy the signature of the constructor.
In example (3) you are passing a number value to constructor expecting a constant reference to pointer. Constant reference is a fixed number, just a memory address. In this case compiler understands the intent and provides the memory address to set in the constructor. As a result you are getting fixed alias of i.
EDIT (for clarity): Difference between (2) and (3) is that &i is not a valid reference to int*, but it is a valid const reference to int*.
In my work the use of const_cast is under some circumstances unavoidable.
Now I have to const_cast some pretty complicated types and actually I don't want to write all this type clutter in the const_cast<Clutter> expressions, especially if Clutter is very long.
My first idea was to write const_cast<>(myType), but my compiler cannot deduce the non-const type of myType. So I thought about helping my compiler and I deviced the following approach, which compiles.
#include <stdlib.h>
#include <iostream>
int main(int, char**) {
const int constVar = 6;
using T = typename std::remove_cv<decltype(constVar)>::type;
auto& var = const_cast<T&>(constVar);
var *= 2;
std::cout << &constVar << " " << &var << "\n"; // Same address!
std::cout << constVar << " " << var << "\n";
return EXIT_SUCCESS;
}
Unfortunately, the program gives me the output 6 12 instead of the expected 6 6, which I really didn't understand?
What is wrong with my approach?
From the documentation of const_cast:
const_cast makes it possible to form a reference or pointer to non-const type that is actually referring to a const object or a reference or pointer to non-volatile type that is actually referring to a volatile object. Modifying a const object through a non-const access path and referring to a volatile object through a non-volatile glvalue results in undefined behavior.
So what you have is undefined behavior.
Also of interest is this note from cv type qualifiers.
const object - an object whose type is const-qualified, or a non-mutable subobject of a const object. Such object cannot be modified: attempt to do so directly is a compile-time error, and attempt to do so indirectly (e.g., by modifying the const object through a reference or pointer to non-const type) results in undefined behavior.
If you have
void foo(const int& a)
{
const_cast<int&>(a) = 4;
}
then
int a = 1;
foo(a);
is perfectly legal, but
const int a = 1;
foo(a);
invokes an undefined behaviour, because in foo, a was originally const.
This is useful in some case (usually when interfacing old C library), but in most cases, you are doing something wrong and should rethink your solution.
And to answer why const_cast<> isn't a thing, I'd say for two reasons. First, when you do const_cast you should really know what you are doing, if some kind of template deduction was allowed, it would make doing unintended mistakes more likely to occur. And secondly const_cast can also be used to remove volatile and how can compiler know what you want to cast away?
When a function parameter is of type lvalue reference lref:
void PrintAddress(const std::string& lref) {
std::cout << &lref << std::endl;
}
and lref is bound to a prvalue:
PrintAddress(lref.substr() /* temporary of type std::string */)
what does the address represent? What lives there?
A prvalue cannot have its address taken. But an lvalue reference to a prvalue can have its address taken, which is curious to me.
Inside the function lref is not a prvalue it is an lvalue and you can take the address of it.
There is a common misconception about rvalues vs. lvalues.
A named parameter is always an lvalue. No matter whether it is a reference type that is bound to an rvalue. Through a const & reference type you can't even tell which kind of value category the object actually has at the point where the function is called. Rvalue references and non-const Lvalue references give you that information:
void foo(std::string& L, std::string&& R)
{
// yeah i know L is already an lvalue at the point where foo is called
// R on the other hand is an rvalue at the point where we get called
// so we can 'safely' move from it or something...
}
The temporary string is a prvalue in the context of the caller (at the point PrintAddress is called). Within the context of the callee (in PrintAddress) lref is an lvalue reference because in this context it actually is an lvalue.
PrintAddress isn't aware of the limited lifetime of the passed argument and from PrintAddress' point of view the object is "always" there.
std::string q("abcd");
PrintAddress(q.substr(1)); // print address of temporary
is conceptually equivalent to:
std::string q("abcd");
{
const std::string& lref = q.substr(1);
std::cout << &lref << std::endl;
}
where the temporary experiences a prolongation of its lifetime to the end of the scope in which lref is defined (which is to the end of PrintAddress function scope in the present example).
what does the address represent? What lives there?
A std::string object containing the passed content.
And is it legal (in C++, and with respect to memory) to write to that address?
No, it would be legal if you'd use an rvalue reference:
void PrintAddressR(std::string&& rref) {
rref += "Hello"; // writing possible
std::cout << &rref << std::endl; // taking the address possible
}
// ...
PrintAddressR(q.substr(1)); // yep, can do that...
The same applies here: rref is an lvalue (it has a name) so you can take its address plus it is mutable.
In short, because the prvalue's lifetime has been extended. By having its lifetime extended - by any reference -, it's an lvalue, and thus can have its address taken.
what does the address represent? What lives there?
The address represents an object, the object referenced by lref.
A prvalue is short lived, it doesn't live for long. In fact, it will be destroyed when the statement creating it ends.
But, when you create a reference to a prvalue (either an rvalue reference or a const lvalue reference), its lifetime is extended. Ref.::
An rvalue may be used to initialize a const lvalue [rvalue] reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
Now it makes actually sense to take its address, as it is an lvalue for all intents and purposes. Now, that the prvalue has an indeterminate lifetime, it is an lvalue.
Taking the address of a prvalue doesn't make sense however, and that's probably why it is disallowed:
The value is destroyed after the next statements, so you can't do anything with the address, except maybe print it out.
If you take the address of something, the compiler is required to actually create the object. Sometimes, the compiler will optimize out variables that are trivial, but if you were to take the address of them, the compiler won't be allowed to optimize them out.
Taking the address of a prvalue will thus result in the compiler being unable to elide the value completely, for no advantages whatsoever (see point 1).
In simple English:
void PrintAddress(const std::string& lref) {
std::cout << &lref << std::endl;
}
Any object that has a name is an lvalue, hence any use of lref within the scope of the funtion above is an lvalue use.
When you called the function with:
PrintAddress(lref.substr() /* temporary of type std::string */)
Of cause, lref.substr() produces a temporary which is an rvalue, but rvalues can bind to (have its lifetime extended by) const lvalue references or rvalue references.
Even if you provided an rvalue overload, for the fact it has a name, its an "lvalue of something" within its scope, example:
#include <string>
#include <iostream>
void PrintAddress(const std::string& lref) {
std::cout << "LValue: " << &lref << std::endl;
}
void PrintAddress(std::string&& `rref`) {
std::cout << "RValue: " << &rref << std::endl; //You can take address of `rref`
}
int main(){
std::string str = "Hahaha";
PrintAddress(str);
PrintAddress(str.substr(2));
}
Just remember:
In C++, any object(whether value type, reference type or pointer type) that has a name is an lvalue
Also know that some expressions produce lvalues too.
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
#include<iostream>
int & fun();
int main()
{
int p = fun();
std::cout << p;
return 0;
}
int & fun()
{
int a=10;
return a;
}
Why is this program not giving error at line no.6 as "invalid conversion from int* to int", as it happens in case we do like this?
int x = 9;
int a = &x;
int& is a type; it means "a reference to int."
&x is an expression; it means "take the address of x." The unary & operator is the address operator. It takes the address of its argument. If x is an int, then the type of &x is "a pointer to int" (that is, int*).
int& and int* are different types. References and pointers are the same in many respects; namely, they both refer to objects, but they are quite different in how they are used. For one thing, a reference implicitly refers to an object and no indirection is needed to get to the referenced object. Explicit indirection (using * or ->) is needed to get the object referenced by a pointer.
These two uses of the & are completely different. They aren't the only uses either: for example, there is also the binary & operator that performs the bitwise and operation.
Note also that your function fun is incorrect because you return a reference to a local variable. Once the function returns, a is destroyed and ceases to exist so you can never use the reference that is returned from the function. If you do use it, e.g. by assigning the result of fun() to p as you do, the behavior is undefined.
When returning a reference from a function you must be certain that the object to which the reference refers will exist after the function returns.
Why is this program not giving error at line no.5 as "invalid conversion from int* to int", as it happens in case we do like this?
That's because you are trying to return the variable by reference and not by address. However your code invokes Undefined Behaviour because returning a reference to a local variable and then using the result is UB.
Because in one case its a pointer and in the other a reference:
int a=&x means set a to the address of x - wrong
int &p=fun() means set p to a reference to an int - ok
Functions in C++ are not same as macros i.e. when you qrite int p = fun() it doesn't become int p = &a; (I guess that is what you are expecting from your question). What you are doing is returning a reference from the function f. You are no where taking address of any variable. BTW, the above code will invoke undfeined behavior as you are returning a reference to the local variable.
You're not returning an int *, you're retuning an int &. That is, you're returning a reference to an integer, not a pointer. That reference can decay into an int.
Those are two different things, although they both use the ampersand symbol. In your first example, you are returning a reference to an int, which is assignable to an int. In your second example, you are trying to assign the address of x (pointer) to an int, which is illegal.