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.
Related
I'm learning C++ and pointers and I thought I understood pointers until I saw this.
On one side the asterix(*) operator is dereferecing, which means it returns the value in the address the value is pointing to, and that the ampersand (&) operator is the opposite, and returns the address of where the value is stored in memory.
Reading now about assignment overloading, it says "we return *this because we want to return a reference to the object". Though from what I read *this actually returns the value of this, and actually &this logically should be returned if we want to return a reference to the object.
How does this add up? I guess I'm missing something here because I didn't find this question asked elsewhere, but the explanation seems like the complete opposite of what should be, regarding the logic of * to dereference, & get a reference.
For example here:
struct A {
A& operator=(const A&) {
cout << "A::operator=(const A&)" << endl;
return *this;
}
};
this is a pointer that keeps the address of the current object. So dereferencing the pointer like *this you will get the lvalue of the current object itself. And the return type of the copy assignment operator of the presented class is A&. So returning the expression *this you are returning a reference to the current object.
According to the C++ 17 Standard (8.1.2 This)
1 The keyword this names a pointer to the object for which a
non-static member function (12.2.2.1) is invoked or a non-static data
member’s initializer (12.2) is evaluated.
Consider the following code snippet as an simplified example.
int x = 10;
int *this_x = &x;
Now to return a reference to the object you need to use the expression *this_x as for example
std::cout << *this_x << '\n';
& has multiple meanings depending on the context. In C and used alone, I can either be a bitwise AND operator or the address of something referenced by a symbol.
In C++, after a type name, it also means that what follows is a reference to an object of this type.
This means that is you enter :
int a = 0;
int & b = a;
… b will become de facto an alias of a.
In your example, operator= is made to return an object of type A (not a pointer onto it). This will be seen this way by uppers functions, but what will actually be returned is an existing object, more specifically the instance of the class of which this member function has been called.
Yes, *this is (the value of?) the current object. But the pointer to the current object is this, not &this.
&this, if it was legal, would be a pointer-to-pointer to the current object. But it's illegal, since this (the pointer itself) is a temporary object, and you can't take addresses of those with &.
It would make more sense to ask why we don't do return this;.
The answer is: forming a pointer requires &, but forming a reference doesn't. Compare:
int x = 42;
int *ptr = &x;
int &ref = x;
So, similarly:
int *f1() return {return &x;}
int &f1() return {return x;}
A simple mnemonic you can use is that the * and & operators match the type syntax of the thing you're converting from, not the thing you're converting to:
* converts a foo* to a foo&
& converts a foo& to a foo*
In expressions, there's no meaningful difference between foo and foo&, so I could have said that * converts foo* to foo, but the version above is easier to remember.
C++ inherited its type syntax from C, and C type syntax named types after the expression syntax for using them, not the syntax for creating them. Arrays are written foo x[...] because you use them by accessing an element, and pointers are written foo *x because you use them by dereferencing them. Pointers to arrays are written foo (*x)[...] because you use them by dereferencing them and then accessing an element, while arrays of pointers are written foo *x[...] because you use them by accessing an element and then dereferencing it. People don't like the syntax, but it's consistent.
References were added later, and break the consistency, because there isn't any syntax for using a reference that differs from using the referenced object "directly". As a result, you shouldn't try to make sense of the type syntax for references. It just is.
The reason this is a pointer is also purely historical: this was added to C++ before references were. But since it is a pointer, and you need a reference, you have to use * to get rid of the *.
I am studying C++ templates, and I got stuck thinking about the interaction between const and types that are arguments to template functions. Specifically, I am thinking about how consts interact with template types when applied outside of the template parameter list.
I have tried looking for this interaction in C++ Primer 5th ed (Lippman) and in the C++11 standard draft, but const in this context is either not explicitly mentioned or (in the case of the standard) rather complex in its description (I'm still somewhat new to C++).
Here is a code example of my problem:
template<typename T>
const T & constify(T & t) {
return t;
}
...
int* i = 0x12345678;
constify(i);
I have two different expectations of the return type:
The deduced return type is const (int *) &, i.e the const is applied afterwards, so that we cannot modify the int pointer but we can modify what it points to.
The deduced return type is const int * &, i.e all declarators and qualifiers are applied all at once instead of as in 1. Here, we can no longer modify the int pointed to by the integer, but we can modify the pointer itself.
For me, the first one makes more sense because it has a natural "substitution-like" rule behind it, similar to typedef. But my question is; which of these (if any) is correct and why?
Template type substitutions are not textual, so do not think of them in terms of the textual type definition.
In your example, T is deduced to be int * - let's call it intptr. You are making const reference to it, so return value becomes const intptr&. That means, that the pointer itself can't be modified through this reference, but the value it points to can be modified.
Last, but not the least, you could have easily verifed your assumptions before asking the question :)
The 1st one is correct, and the return type would be int * const &, i.e. the reference to const pointer to non-const int; not const int * &, i.e. the reference to non-const pointer to const int.
const is qualified on T itself, when T is a pointer const T would be a const pointer but not a pointer to const pointee.
const consistent location is on the right of the type. Some declarations, like variables, also allow putting const on the left, but others, like member function definitions, only allow const on the right.
If you always put/apply const on the right it helps you to think about it in the right way. This is the reason many boost libraries put const on the right.
constify apples const on the right, hence, it turns T that is int* into int* const - a constant pointer to non-constant int. And apply the reference on the right of that: int* const&.
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.
I have a function defined as:
void func(string & str_alias)
{...}
And in my main function
int main()
{
string a;
func((a="Cat said: ")+"Meow");
}
The compiler would report that
no known conversion for argument 1 from ‘std::basic_string<char>’ to ‘std::string& {aka std::basic_string<char>&}’
Though I know if I change the main function into:
int main()
{
string a;
func(a=((a="Cat said: ")+"Meow"));
}
The code would pass with no issues. But I still wonder why the returned string cannot be passed to the function as a reference. Why do I have to assign it to another string variable?
Thanks.
As long you don't need to change the passed reference, you could easily avoid this by changing your function signature to
void func(const string & str_alias)
// ^^^^^
{...}
and simply call
func(string("Cat said: ") + "Meow");
(see live demo)
If you'll need to change the reference parameter, you must have an lvalue to be modified. Nevertheless writing
func(a=string("Cat said: ")+"Meow");
is sufficient (see the live demo).
If you make it take const reference to std::string, it should compile.
This is because the last thing you do in the first function call is calling std::string operator+(const std::string&, const char*), which as you see returns std::string, not reference, and since it not stored anywhere, it is rvalue, which can't be bound to lvalue-reference.
The second example compiles, because the last thing you do is assign it to the variable a, which calls std::string& operator=(const char*), which as you can see returns reference, so it can be used as non-const reference by itself.
Thanks to 0x499602D2 for correction.
I tried following code :
#include<iostream>
#include<string>
using namespace std;
string f1(string s)
{
return s="f1 called";
}
void f2(string *s)
{
cout<<*s<<endl;
}
int main()
{
string str;
f2(&f1(str));
}
But this code doesn't compile.
What I think is : f1 returns by value so it creates temporary, of which I am taking address and passing to f2.
Now Please explain me where I am thinking wrong?
The unary & takes an lvalue (or a function name). Function f1() doesn't return an lvalue, it returns an rvalue (for a function that returns something, unless it returns a reference, its return value is an rvalue), so the unary & can't be applied to it.
It is possible to create (and pass) a pointer to a temporary object, assuming that you know what you are doing. However, it should be done differently.
A function with return value of non-reference type returns an rvalue. In C++ applying the built-in unary operator & to an rvalue is prohibited. It requires an lvalue.
This means, that if you want to obtain a pointer to your temporary object, you have to do it in some other way. For example, as a two-line sequence
const string &r = f1(str);
f2(&r);
which can also be folded into a single line by using a cast
f2(&(const string &) f1(str));
In both cases above the f2 function should accept a const string * parameter. Just a string * as in your case won't work, unless you cast away constness from the argument (which, BTW, will make the whole thing even uglier than it already is). Although, if memory serves me, in both cases there's no guarantee that the reference is attached to the original temporary and not to a copy.
Just keep in mind though creating pointers to temporary objects is a rather dubious practice because if the obvious lifetime issues. Normally you should avoid the need to do that.
Your program doesn't compile because f1 has a parameter and you're not passing any.
Additionally, the value returned from a function is an rvalue, you can't take its address.
Try this:
int main()
{
string str;
string str2 = f1(str); // copy the temporary
f2(&str2);
}