Look at my code, I can cast GoodMan pointer to a Man pointer and pass in to AddMan function using two steps. But when I uses one step to do that, It doesn't work. Instead, It needs to cast to the Man pointer reference explicitly not only to the Man pointer. What is the reason behind this?
class Man{
};
class GoodMan: public Man
{
};
void AddMan(Man*& man){};
int main()
{
GoodMan* good_man = new GoodMan() ;
//This works
Man* pMan2 = (Man*)good_man;
AddMan(pMan2);
//So I think this should work, But doesn't work
AddMan((Man*)good_man);
//But this works
AddMan((Man*&)good_man);
return 0;
}
C++ doesn't allow binding rvalues (bacically temporaries) such s (Man*)good_man to non-const lvalue references such as Man*&. In your code, pMan2 is an lvalue, so this can bind to the non-const reference parameter of AddMan. But (Man*)good_man is an rvalue. This is the cause of the error.
If you change the signature of AddMan to take a const lvalue reference, your code would compile.
void AddMan(Man* const& man){};
Of course, this doesn't allow you to change the pointer passed as argument. On the other hand, your solution, to cast to lvalue reference, "works" as far as making the code compile. But you have to ask yourself what it would mean to modify the pointer inside of the AddMan function.
AddMan((Man*&)good_man); // "works" by pretending everything is OK. It isn't.
AddMan((Man*&)good_man); causes undefined behaviour. This is aliasing a GoodMan * as a Man * which is not permitted. It is a reinterpret_cast.
Although GoodMan is derived from Man , GoodMan * is not "derived" from Man *, they are incompatible types.
Example:
AddMan(static_cast<Man*&>(good_man)); // error: invalid static_cast
AddMan(reinterpret_cast<Man*&>(good_man)); // no error, but runtime UB
To help "sniff out" this problem, think about the void AddMan(Man*& man); function. It accepts a reference to Man *. It has to be called with a variable that is actually a Man *. The reference has to bind to a Man * specifically. In your original code, what has the reference bound to? good_man is not a Man *.
This is because (Man*)good_man is an r-value (i.e. a temporary) while (Man*&)good_man and pMan2 are l-values.
When binding a reference:
l-value reference Man& = l-value
r-value reference Man&& = r-value
const l-value reference const Man& = any type of reference
const r-value reference const Man&& = const and non-const r-value
What you have as the function parameter is a l-value reference, which therefore can only bind to an l-value.
Thank you for reading.
(Man*)good_man is an rvalue and hence can't bind to a reference of Man*. (Man*&)good_man on the other hand has already been casted to a compatible type and works fine. Note that if you change the signature of AddMan to take a const reference, all your examples will work fine.
Related
I am trying to get a better understanding of what "passing by reference" really does in c++. In the following code:
#include <iostream>
void func(int& refVar) {
std::cout << refVar;
}
int main() {
int val = 3;
func(val);
}
How does func taking in a reference change the behavior of val when func is called? Is the val variable implicitly converted to a "reference" type and then passed into the function, just like how val would be converted to a float if func took in a float instead of an int? Or is it something else?
Similarly, in this code:
int val = 3;
int& ref = val;
How is a reference of val assigned to ref? Is there implicit type conversion that can be also achieved manually using a function, or does the compiler realize without converting that ref is a reference and that it needs to store the address of val?
Why don't you just try it?
https://godbolt.org/z/8or3qfd5G
Note that the compiler could do implicit conversion and pass a reference to the temporary.
But the only (good) reason to request a reference is to either store the reference for later use or modify the value. The former would produce a dangling reference and the later would modify a value that will be deleted when the function returns and can never be accessed. So effectively this construct is just bad.
The C++ gods have therefore decided that you aren't allowed to use this. Implicit conversion produces an rvalue and you can only bind a const reference to an rvalue.
Binding a rvalue to a const reference is still dangerous. You should not store a const reference for later use because it can become dangling.
Update: I noticed I never explained how calling a function taking a reference or assigning to a reference works. It's basically both the same thing.
A reference just gives something a(nother) name. There is no type change or casting or anything involved. So when you have
`func(val)`
then for the duration of the function the value in val has a second name refVar. Same with int & refVal = val;. There now is a second name for the value in val called refVal.
Afaik they are totally interchangeable.
Note: In a function call how it works is implementation detail but most compilers pass a int * to the function under the hood.
I have a class taking variables by reference. A function in the class needs to call another function that prints the object. The question arises when passing reference object from process() to disp(). Can I pass a reference from one function to another function? How to accomplish this using reference and what are best practices in such cases?
(I know one can take other approaches, such as using pointers or passing by value to class. But, I want to know solution with reference.)
class Abc
{
double &a, &b;
public:
Abc(double &var1, double &var2): a(var1), b(var2) {}
void process()
{
//call disp()
disp(a); //Question
}
void disp(double &var)
{
std::cout << var;
}
};
int main()
{
double x=2.2, y=10.5;
Abc obj1(x,y);
obj1.process(); //question
return 0;
}
Can I pass a reference object to a function?
Pedantic point: There is no such thing as a "reference object". References are not objects.
But yes, it is possible to pass a reference to a function.
The question arises when passing reference object from process() to disp()
You already pass a reference there. That part of the program is correct.
You do have a problem here:
Abc::Abc(double&, double&);
float x=2.2, y=10.5 // anohter bug: probably intended to have a semicolon here
Abc obj1(x,y);
When object of one type (float) is bound to a reference of another type that is not related through inheritance (double&), the operand is converted to the target type (double). The result of the conversion is a temporary r-value. Non-const l-value references cannot be bound to r-values so therefore the program is ill-formed.
Even if the reference could be bound (for example, if you use a language extension that allows it, or if you used const references instead), the lifetime of the temporary would only extend for the lifetime of the reference variable which it was bound to, which is the argument of the constructor. After the constructor was finished, the member references would be referring to the temporary whose lifetime has already ended and therefore using those references would have undefined behaviour.
Simple solution: Use variables of same type as the reference: double x=2.2, y=10.5.
Why does the first line not compile while the second line compiles?
float& t = float(10); // initial value of reference to non - const must be an lvalue
string& w = string("gg");
Does this imply that the string constructor returns a lvalue?
Constructor by the definition does not have a return value. What you're trying to perform is making a reference to a temporary value which is not allowed.
Good article to understand both lvalue and rvalue references is C++ Rvalue References Explained.
What you are getting is not what you are expecting:
float& ref = float(10);
What does happen here? float(10) creates a temporary float (not bound to anything), it is thus an rvalue. Therefore you cannot bind it to a non-const lvalue reference.
float const & cref = float(10); // OK!
The mechanics involved in this are the same for std::string. I am puzzled that you do not get a compiler error on the string example (I do with gcc 5.3.0)
Note that this has nothing to do with a so-called "constructor return value", because constructors have no such thing.
today my teacher showed me the following example:
CFraction operator*(const CFraction &a, const CFraction &b){
CFraction x;
x.setNumerator(a.getNumerator()*b.getNumerator());
x.setDenominator(a.getDenominator()*b.getDenominator());
return x;
}
// ...
void main(){
CFraction b1,b2(3,7),b3(5,8);
b2=b1*3;
b2=3*b1;
}
He said that the above code would be working fine, but if you change the method to the following:
CFraction operator*(CFraction&,CFraction&);
it wouldn't work. Why is that?
Hope you can explain it to me.
Thanks in advance.
it wouldn't work. Why is that?
Let's look into your expression:
b2=b1*3;
is equal to:
b2=operator*(b1,3);
as second type is not type of CFraction it has to be converted to it ie:
b2=operator*(b1,CFraction(3));
CFraction(3) creates temporary object of type CFraction. Temporary is not lvalue so cannot be passed as lvalue reference - CFraction & and if you define your function with non const references (aka lvalue references) it would not work in this case.
You are basically asking, "why should my arguments be const?"
There is actually a good reason for this in this scenario. In c++, whenever you have a function argument with a const reference, (amongst other things) it means you can pass in a literal as an argument into that function (assuming the capacity for a type conversion is available).
For example, if you don't use the const reference, you can still do something like:
CFraction b1,b2(3,7),b3(5,8);
b3=b1*b2;
In the prior example, you passed in (non constant) references to objects b1 and b2. That's alright because this expression makes sense. However, you could not do the following:
CFraction b1,b2(3,7),b3(5,8);
b3=b1*3;
Why? Because 3 is a literal. Whenever you don't include the const reference to CFraction, you cannot pass literals into that function as an argument. Using const reference enables you to pass in literal values.
I've got quite a nifty get fnc which returns pointer to 'a type'. Now I would like to reuse this fnc in fnc set to set some value to this type returned by get:
template<class Tag,class Type>
set(Type t, some_value)
{
get<Tag>(t) = value;
}
The only problem I have is that: Because get returns pointer and not reference to a pointer the return type is a rvalue which for most cases is fine but not for this. Is there a way to somehow change the returned value into lvalue?
You can simply use this:
*get<Tag>(t) = value;
The result of dereferencing a pointer is an l-value.
Dereferencing a pointer (with the * operator) yields a reference. The type of the reference depends on the type of the pointer. const T * becomes const T &, while T * becomes T &.
So, if get returns a pointer to a non-const variable, you can write:
*get<Tag>(t) = value;
If get does not meet such requirement, and you can't change it, you'll have to give a set method instead.