return struct value in C++ - c++

When I studied const reference type in C++,
I learned returning reference type make use local variable in main function,
so I tested how returned normal struct type work in lvalue. so I expected this source won't compile,
but it compiled well... :(
so I have two question
"Is returned struct variable?, why this code compiled well?"
"if returned struct is not variable, why this code compiled?, I assumed that returned value is rvalue.."
#include<iostream>
#include<cstring>
using namespace std;
struct travel_time
{
int hour;
int min;
};
travel_time sumTime(travel_time, travel_time);
int main(void)
{
travel_time p1, p2,sum;
cin >> p1.hour >> p1.min;
cin >> p2.hour >> p2.min;
sum=sumTime(p1, p2);
cout << sum.hour << "hour " << sum.min<<"min";
sumTime(p1, p2) = p1; //********** why it works? **********
return 0;
}
travel_time sumTime(travel_time t1, travel_time t2)
{
travel_time sum;
sum.hour = t1.hour + t2.hour+(t1.min+t2.min)/60;
sum.min = (t1.min + t2.min) % 60;
return sum;
}

Is returned struct variable?, why this code compiled well?
This doesn't make much sense.
travel_time sum; is a variable. When you call sumTime, you return a copy of this variable, and that copy itself isn't a variable. So the answer is no, the result of calling the function isn't a variable.
I have a feeling what you really wanted to ask is if the result of calling the function is an lvalue - it's not, the result is an rvalue (more specifically, prvalue).
if returned struct is not variable, why this code compiled?, I assumed that returned value is rvalue..
This again makes no sense, unless I replace "variable" with "lvalue".
In C++, you can assign to rvalues of class types (but not of privitive types such as int). Note that struct travel_time is a class, despite using the word "struct". Strictly speaking, there are no structures in C++, only classes.
There are ways to prevent this kind of assignment for your class, but you didn't do that.

Related

What happens to a parameter if it is passed twice? Once by value and once by reference? Will it be modified or not?

#include <iostream>
#include <fstream>
using namespace std;
ifstream fin("BAC.TXT");
void eval(int a, int b, int &rez)
{
rez = a + b;
}
int main()
{
int nr;
int s;
fin >> s;
while (fin >> nr)
eval(s, nr, s);
cout << s << '\n';
return 0;
}
So I have this code snippet. I am reading numbers from a file and keeping track of their sum using a given function called “eval”. I know it could be considered bad code to pass a parameter twice (in such a given instance) rather than using another variable (not sure, though, if it is bad code or not, in my case). My problem is: will it change the value of variable s? Again, I am passing it once by value and once by reference! I have written the code on my PC and it does change the value of s. Now my question would be: why? If I am asking this in a right way: what happens “in the background”?
The fact that a is a copy of s is actually a red herring. Maybe that caused your confusion, but its just a copy. Consider that you could call the function like this
auto temp = s;
eval(temp,nr,s);
to get the exact same result. Inside the function rez is a reference to s, hence modifications on it will be reflected on s in main. In other words, rez is an alias for s, while a has no relation whatsoever to s, they just happen to hold the same value.
This question is related to the behavior of functions parameters and reference/pointers.
When you pass a value to a function, it copies the value you give to a local variable inside the function.
in your case, when you pass 's', it copies it to 'a' like if you would do it in your main: int a = s (this is what is going on)
The same applies to the reference. when you pass 's' to the third parameter, it does this:
int &rez = s
When you pass it by reference, it is sort of taking the address of the variable instead of the value itself and copying this address to a local variable that is already dereferenced. (pointer stuff)
So the value is changed.
See the video related to pointers and then reference on 'the Cherno Project' youtube channel for better comprehension of the subject:
Pointers
References
Short answer: Yes, s will be changed.
Long answer: Let me rewrite your code snippet a little bit to illustrate what is happening with the function parameters during a call:
#include <iostream>
#include <fstream>
using namespace std;
ifstream fin("BAC.TXT");
void eval(int a, int b, int &rez)
{
rez = a + b;
}
int main()
{
int nr;
int s;
fin >> s;
while(fin >> nr) {
int a = s;
int b = nr;
s = a + b;
}
cout << s << '\n';
return 0;
}
So, basically, a and b are just copies of s and nr respectively. However, res is s.
IMO, this is what I would expect :
Copy s to a new var l_S.
Copy nr to a new var l_Nr.
Copy &s to new var l_Ptr.
Add l_Nr and l_S.
Save the result in *l_Ptr.

Pointer Confusion with C++

I am very confused with c++ pointers and reference operators. My main confusion is the following (simple ) code:
#include <iostream>
using namespace std;
void changeInt(int &a)
{
a *= 3;
}
int main()
{
int n = 3;
changeInt(n);
cout << n << endl;
return 0;
}
Mainly, I am confused as to why changing the address (&a) changes the actual variable (n). When I first attempted this problem this was my code:
#include <iostream>
using namespace std;
void changeInt(int &a)
{
*a *= 3;
}
int main()
{
int n = 3;
changeInt(n);
cout << n << endl;
return 0;
}
But this gives me an error. Why is it that when I change the address it changes the variable, but when I change the value pointed by the address I get an error?
Your second example is not valid C++, you can only dereference a pointer (or an object whose type overload operator*, which is not your case).
Your first example pass the parameter by reference (int &a is not "the address of a", it is a reference to a), which is why a change to a really is a change to the object being passed by the function (in you case, n)
The ampersand (&) in that context means a reference, not the "address". E.g.:
int some_int;
int & a = some_int; // Declare 'a', a reference to 'some_int'
int * p = &some_int; // '&' in this context is "the address of" 'some_int'
A reference is equivalent to a pointer in many ways, but it behaves like a value type.
See this thread and the wikipedia entry to learn more.
The ampersand indicates that a variable is passed by reference to your function -- but inside the function the variable is treated as if it were passed by value. This is syntactic sugar, to make writing code that accepts references simpler to understand.

Inconsistency in passing by reference - how does this simple example of passing by reference work?

I had a simple question and was hoping for the underlying logic behind passing by reference.
Here's one code (let's call it Code1):
void fn(int& a)
{
a = 6;
}
int main()
{
int b = 5;
fn(b);
cout << b;
}
Here's another code (Code2):
void fn(int* ptr)
{
*ptr = 6;
}
int main()
{
int b = 5;
fn(&b);
cout << b;
}
And a pass by value code (Code 3):
void fn(int a)
{
a = 6;
}
int main()
{
int b = 5;
fn(b);
cout << b;
}
Here goes my question. Intuitively, I see that while passing by value (Code3), the values are copied ie a would just have taken/copied into itself the value of b. Thus, as a general rule, I see that value passed is just copied always to the called function (here fn). Even with the pointer code (ie Code2), the first line of Code 2 ensures that int *ptr = &a;
I don't understand how this would work in Code1. Saying that &a = b makes no sense. Is this an exception, or does this fit into a rule that is consistent with the cases discussed in the paragraph above?
Thanks!
In this function:
void fn(int &a) {
a=6;
}
the term "&a" does not mean "the address of the variable a". It means "a reference called a". Code 1 and Code 2 are effectively the same (but note that the function in Code 2 can be passed an invalid pointer, which is (almost) impossible for Code 1).
For most intents and purposes, a reference is just a pointer in disguise. Different syntax, same effect (mostly).
Conceptually, in your first case what happens is that the same variable has two labels: b, visible within the scope of main(); and a, visible within the scope of fn.
You don't have to worry about what the compiler does "behind the scenes" to implement this concept.
If you mentally promote the compiler's "behind the scenes" actions to actually being imagined principles of C++, e.g. "the reference is a pointer in disguise", then it leads you to get confused about what is actually a pretty simple concept: the ability to give multiple names to a variable.
It is nothing special being a function parameter; e.g. you could write in main():
int a;
int &c = a;
which is exactly equivalent to:
int c;
int &a = c;
In both cases there is an int variable with two labels, a and c.

could not convert from 'unsigned int' to 'unsigned int&'

I'm creating a method and one parameter of that method asks for a reference of an unsigned int, but I want to put a default value on that parameter. For example:
#include <iostream>
using namespace std;
class A {
public:
void sender();
private:
unsigned int score = 10;
};
class B {
public:
void receiver(unsigned int & score);
};
void A::sender() {
cout << "Before: " << score << endl;
B b;
b.receiver(score);
cout << "After: " << score << endl;
}
void B::receiver(unsigned int & score) {
score = 100;
}
int main() {
A a;
a.sender();
return 0;
}
Live demo: in here
The error happen when I do this:
void receiver(unsigned int & score = 10u);
And the compiler returns:
error: could not convert ‘10u’ from ‘unsigned int’ to ‘unsigned int&’
Live demo: in here
You cannot assign a literal1 to a non-const reference.
There are two scenarios where one will suit your situation:
You intend to modify the argument being passed to receiver()
If that is the case, the use a non-const reference (unsigned int & score) without the default parameter. In situations where you pass a literal or temporary object to it, then it will result in a compiler error.
a.receiver(10); // Error
The above wouldn't make any much sense considering that you want to modify that argument (you wouldn't see the modification if C++ had allowed that2).
You intend to just use the parameter in a read-only fashion
Just use plain, non-reference, unsigned int, because const unsigned int& score is just a pain to write. In cases where you determine that an object is expensive to copy, then that is the time where you should have the parameter a const reference.
UPDATE: There are some cases where you want to modify something, but that something may or may not exist. In cases like that, you may want to use a non-owning pointer as the parameter.
// Declaration
void receiver(unsigned int* score = nullptr);
void B::receiver(unsigned int* score) {
if(score) *score = 100;
}
...
a.receiver(); // Uses the default parameter
unsigned int x;
a.reciever(&x);
In this case, it only assigns to score when it points to some (assumed) valid variable. Pointers aren't that bad at all.
UPDATE 2: However, as #Potatoswatter have pointed out, you may be better off with function overloading.
void B::receiver() {
// Do something else
}
void B::receiver(unsigned int& score) {
score = 100;
}
You should use this in cases where you want your overloads to behave differently on different parameters.
However again, I prefer the first, non-default parameter option, rather than the pointer option and the overloading option, as it requires that the caller provide a parameter, which is much better when you are modifying something through a function.
UPDATE 3: You should also consider to have your function return the value instead of modifying it through a parameter. In cases where you don't need the current state of the object being modified, having a function return a value is much more intuitive. One caveat though is that the caller may forget to capture (assign) the return value, which could be dangerous if you are using that value as some resource ID to free something up.
1 And in general, a temporary object.
2 And the universe would probably blow up if 10 was magically transformed into 100 ;)
You want the argument type to be const unsigned int&. Otherwise, you can do something crazy, like try to assign 10 = 20, which doesn't make sense.
And that happens to be exactly that you did. The score = 100 line doesn't seem to be what you actually meant.
the value "10" isn't a reference- it's a value. By having a by-reference parameter, it must be called with a reference. Using a default parameter means you could call the function without specifying the parameter and the compiler would use the default.
Similarly, calling b.receiver(10); is not valid, but
int someInt = 10;
b.receiver(someInt);
is valid.

C++ compiler warning(?) when passing uninitialized local variable to function

I'm very new to C++ so I'm hoping someone would shed some light. I came across several similar topics but I just need clarification.
So it seems it's valid to pass a local string that has been declared but not initialized to a function. But why does compiler complain when you try it with int or float??
Whether it's string, float, or int, memory address gets referenced when it is declared even tho it may be "garbage"
#include <iostream>
using namespace std;
void load(int);
int main()
{
int salary;
load(salary);
return 0;
}
void load(int sal)
{
cout << "your salary: " << endl;
cin >> sal;
cout << sal << endl;
}
If I declare int or float as global variable, it works as expected without any warnings.
So then is it a better practice to declare variable in global space (I hope not)?
So putting it in global, it works :
#include <iostream>
using namespace std;
int salary;
void load(int);
int main()
{
load(salary);
return 0;
}
void load(int sal)
{
cout << "your salary: " << endl;
cin >> sal;
cout << sal << endl;
}
ok, another example to show that uninitialized global variable works when passing to function as value : (going off of David's comment)
int foo;
int returnit(int j)
{
cout << "your salary";
cin >> j;
return j;
}
int main()
{
int k = returnit(foo);
cout << k;
return 0;
}
anyways, lesson here is to initialize primitive data types before passing to functions.
So it seems it's valid to pass a local string that has been declared but not initialized to a function. But why does compiler complain when you try it with int or float??
If by "string" you mean a std::string object, it's because objects are never uninitialized. When you do:
std::string s;
then the default constructor of std::string is invoked, and the object is initialized.
Variables of primitive data types (such as int and float), unless declared to have static storage duration, will have garbage if not explicitly initialized, however. Making attempts to read and use that garbage rightly trigger warnings. (Variables of primitive data types that do have static storage duration (i.e., global variables or variables declared as static) are implicitly initialized to 0.)
So then is it a better practice to declare variable in global space (I hope not)?
No, a better practice would be to initialize your variables.
The best way to explain why you should never use an uninitialized variable in a function call is because this is logically invalid and sometimes both logically invalid and the syntax is invalid.
To see if your program is logically valid use the box method and trace your program. Global box has global variables in it, int main has its own box which holds its local variables, and when functions are called create a new box for that function and fill it with its parameters and local variables. Go line by line and change variable values as they change in the program. Remember to use only constants as global variables. When writhing a function be careful when you pass by reference that you understand that the function is given that variables address in memory and can change that value. When using pass by value use this kind of syntax in your functions declaration:
int Multiplication(const int Var1, const int Var2);
This protects the values you pass to multiplication.The syntax of c++ is made for speed, and it does not keep you from being logically incorrect.
int salary;
load(salary);
What value do you think you are passing to load here? You're passing a nonsense value.
To be clear, you are not passing an "uninitialized variable", you are passing the value of an uninitialized variable. If you do:
int j=3;
load(j);
You are passing the value of j, that is, 3, to load. If you don't specify otherwise, C++ passes by value.
You'd have the same problem with a global variable:
int foo;
int returnit(int j)
{
return j;
}
int main(void)
{
int j=returnit(foo);
What value do you think j should have here?! You still have to initialize a variable to some particular value before you can pass its value to a function.
To add on David's answer, what you probably wanted to do (I'm guessing) is to modify the original salary variable. To do this you need to pass a reference to the variable, like this:
void load(int& sal)
{
cout << "your salary: " << endl;
cin >> sal;
cout << sal << endl;
}
Note the '&' after int in the function signature.
Also note that you use 'salary' in load() where it's not defined. You should use 'sal' instead.
That way the compiler knows that load() receives a reference to a variable, so if you modify it inside the function, the variable you sent to the function (salary) also changes.
The call itself to the function remains the same.