C++ - Does passing by reference utilize implicit conversion? - c++

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.

Related

Reference from literal

I am calling a function with the signature
void setValue(int& data)
I would like to pass a literal number to it:
setValue(1);
But I get:
error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
Is there a way I can make this work without changing the function (it's in a library) and without assigning each literal value to a variable?
Assuming setValue does not actually modify its argument and just has a wrong signature which you cannot change, here is an approach which is not thread-safe among other things:
#include <iostream>
void setValue(int &i)
{
std::cout << "i = " << i << std::endl;
}
int& evil(int i)
{
static int j;
j = i;
return j;
}
int main()
{
setValue(evil(1));
setValue(evil(2));
}
When you declare the argument as being an int&, you are saying that the function called can change the value and the caller will see the change.
So it is no longer valid to pass a literal value then because how could the function possibly change the given value of a literal?
If you don't want the setValue to be able to change the given value, make the argument either be an int or const int&. And if you do want the setValue function to be able to change the value, then the caller must declare a non-const variable to hold the int and pass in that.
Can I change something at the call site to make it work
The problem with your code is that you declared your function to expect a reference, which means the compiler has to prepare the code to allow the function to change whatever you pass into it at the call site. So yes, sure, you can declare a variable, set it to 1 and call your function with it.
Contrast this with a constant reference in the declaration, where the compiler knows you won't change it inside the function, and then you can pass a literal in without issues. In fact, any logical, thought out design will make setters accept constant parameters because it won't change them, it will just store a possibly processed value in its state.
The answer to „what do I do if a library has a bad interface and I can't change it“ is usually „write a wrapper“. Assuming this is a method of some class BadLibraryClass, you could do something like:
class Wrapper {
public:
BadLibraryClass inner;
setValue(int i) {
inner.setValue(i); // i is an lvalue
}
};
This is just a crude example. Perhaps inner is better off being a pointer, a reference or even a smart pointer. Perhaps you want a conversion operator to BadLibraryClass. Perhaps you can use inheritance to expose other methods of BadLibraryClass.
Two options:
Use the result of assignment:
static int _data;
void myCall() {
setValue((_data = 3));
}
Write a wrapper:
struct setValueW {
int _data;
// constructor
setValueW(int _data) : _data(_data) {
setValue(_data);
}
// if you want to call it again
void operator()() {
setValue(_data);
}
};
void myCall2() {
setValueW(3);
}
AFAIK, references keeps the addresses of the variable. 1 is not variable. It is temporary.
Take a look this article(this is a quote from this site)
c++11 introduced a new kind of reference variable -- an r-value reference
To declare one, use && after a type
int & // type designation for an L-value reference
int && // type designation for an R-value reference
L-value references can only refer to L-values
R-value references can reference to R-values (temporaries)
int x, y, z; // regular variables
int & r = x; // L-value reference to the variable x
int & r2 = x + y; // This would be ILLEGAL, since x + y is an R-value
int && r3 = x + y; // LEGAL. R-value reference, referring to R-value
So you can use (But this is not useful. It may be more useful if you write this in plain without rvalue or lvalue.):
void setValue(int&& data)
setValue(1);
Or you can use that:
void setValue(int& data)
int a = 11;
setValue(a);
Don't forget for second example. If you change the value of data parameter. You will have change the a variable value.
No, you can't.
An lvalue reference like that binds to a variable (roughly speaking).
Your literal is not such a thing. It never had a name, and may not even have a home in memory.
Your two options are the two things you ruled out, I'm afraid.
For what it's worth, this is not your fault: that is a rather poor setter. It should take const int& (which will automatically create a nice temporary variable for you out of the literal!), or even just const int.

C++ How to pass reference to function

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.

Pointer cast while pass by referance

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.

Assigning value to function returning reference

#include<iostream>
using namespace std;
int &fun()
{
static int x = 10;
return x;
}
int main()
{
fun() = 30;
cout << fun();
return 0;
}
Function fun() is returning value by reference but in main() method I am assigning some int to function. Ideally, a compiler should show an error like lvalue required but in above case the program works fine. Why is it so?
It's loose and sloppy language to say "a function returns something". It's OK as a shorthand if you know how to work with that, but in this case you get confused.
The more correct way to think about it is that you evaluate a function call expression. Doing that gives you a value. A value is either an rvalue or an lvalue (modulo details).
When T is an object type and you evaluate a function that has return type T, you get a value of type T which is an rvalue. On the other hand, if the function has return type T &, you get a value of type T which is an lvalue (and the value is the thing bound to the reference in the return statement).
Returning a reference is quite useful.
For example it's what std::map::operator[] does. And I hope you like the possibility of writing my_map[key] = new_value;.
If a regular (non-operator) function returns a reference then it's ok to assign to it and I don't see any reason for which this should be forbidden.
You can prevent assignment by returning a const X& or by returning X instead if you really want.
You can rewrite the code using pointers, which might be easier to understand:
#include<iostream>
using namespace std;
int *fun() //fun defined to return pointer to int
{
static int x = 10;
return &x; // returning address of static int
}
int main()
{
*fun() = 30; //execute fun(), take its return value and dereference it,
//yielding an lvalue, which you can assign to.
cout << *fun(); //you also need to dereference here
return 0;
}
References can be very confusing from a syntax point of view, as the dereferencing of the underlying "pointer" is implicitly done by the compiler for you. The pointer version looks more complicated, but is clearer or more explicit in its notation.
PS: Before someone objects to me regarding references as being a kind of pointer, the disassembly for both code versions is 100% identical.
PPS: Of course this method is a quite insidious breach of encapsulation. As others have pointed out, there are uses for this technique, but you should never do something like that without a very strong reason for it.
It works becuse the result of that function is an lvalue. References are lvalues. Basically, in the whole point of returning a non-const reference from a function is to be able to assign to it (or perform other modifications of referenced object).
In addition to other answers, consider the following code:
SomeClass& func() { ... }
func().memberFunctionOfSomeClass(value);
This is a perfectly natural thing to do, and I'd be very surprised if you expected the compiler to give you an error on this.
Now, when you write some_obj = value; what really happens behind the scenes is that you call some_obj.operator =(value);. And operator =() is just another member function of your class, no different than memberFunctionOfSomeClass().
All in all, it boils down to:
func() = value;
// equivalent to
func().operator =(value);
// equivalent to
func().memberFunctionOfSomeClass(value);
Of course this is oversimplified, and this notation doesn't apply to builtin types like int (but the same mechanisms are used).
Hopefully this will help you understand better what others have already explained in terms of lvalue.
I was buffled by similar code too - at fist. It was "why the hell I assign value to a function call, and why compiler is happy with it?" I questioned myself. But when you look at what happens "behind", it does make sense.
As cpp and others poined out, lvalues are "memory locations" that have address and we can assign values to them. You can find more on the topic of lvalues and rvalues on the internet.
When we look at the function:
int& fun()
{
static int x = 10;
return x;
}
I moved the & to the type, so it's more obvious we are returning a reference to int.
We see we have x, which is lvalue - it has address and we can assign to it. It's also static, which makes it special - if it wasn't static, the lifetime (scope) of the variable would end with stack unwinding upon leaving the function and then the reference could point to whatever black hole exists in the universe. However as x is static, it will exist even after we leave the function (and when we come back to the function again) and we can access it outside of the function.
We are returning reference to an int, and since we return x, it's reference to the x. We can then use the reference to alter the x outside of the function. So:
int main()
{
fun();
We just call the function. Variable x (in scope of fun function) is created, it has value of 10 assigned. It's address and value exist even after function is left - but we can't use it's value, since we don't have it's address.
fun() = 30;
We call the function and then change the value of x. The x value is changed via the reference returned by the function. NOTE: the function is called first and only after the function call was completed, then, the assignment happens.
int& reference_to_x = fun(); // note the &
Now we (finally) keep the reference to x returned by the function. Now we can change x without calling the function first. (reference_to_x will probably have the same address as the x have inside the fun function)
int copy_of_x = fun(); // no & this time
This time we create new int and we just copy the value of x (via the reference). This new int has its own address, it doesn't point to the x like reference_to_x is.
reference_to_x = 5;
We assigned x the value 5 through the reference, and we didn't even called the function. The copy_of_x is not changed.
copy_of_x = 15;
We changed the new int to value 15. The x is not changed, since copy_of_x have its own address.
}
As 6502 and others pointed out, we use similar approach with returning references a lot with containers and custom overrides.
std::map<std::string, std::string> map = {};
map["hello"] = "Ahoj";
// is equal to
map.operator[]("hello") = "Ahoj"; // returns reference to std::string
// could be done also this way
std::string& reference_to_string_in_map = map.operator[]("hello");
reference_to_string_in_map = "Ahoj";
The map function we use could have declaration like this:
std::string& map::operator[]( const std::string& key ); // returns reference
We don't have address to the string we "stored" in the map, so we call this overridden function of map, passing it key so map knows which string we would like to access, and it returns us reference to that string, which we can use to change the value. NOTE: again the function is called first and only after it was completed (map found the correct string and returned reference to it) the assignment happens. It's like with fun() = 10, only more beatiful...
Hope this helps anyone who still woudn't understand everything even after reading other answers...
L-value is a locator-value. It means it has address. A reference clearly has an address. The lvalue required you can get if you return from fun() by value:
#include<iostream>
using namespace std;
int fun()
{
static int x = 10;
return x;
}
int main()
{
fun() = 30;
cout << fun();
return 0;
}

return type in c++

#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.