question on operator overloading - c++

I am trying to understand a program, which includes the following definition for a function f
void f(String S, const String& r)
{
}
Here String in the argument stands for a class. I am confused on the difference between the definitions of these two arguments: "String S" and "const String& r". S should represent an object of class String, then how about r?
In more detail, the f is defined as
void f(String S, const String& r)
{
int c1 = S[1]; // c1=s.operator[](1).operator char( )
s[1] ='c'; // s.operator[](1).operator=('c')
int c2 = r[1]; // c2 = r.operator[](1)
r[1] = 'd'; // error: assignment to char, r.operator[](1) = 'd'
}
This code snippet is to show how the operator overload, but these comments does not help me much. For instance, why r[1]='d' is nor correct? Thanks for helping understanding it.

const String& r is a constant reference to String r. Within the function, you access r just like a String. The difference is that it is actually a reference to the object passed to the function, while S will be a copy of the object passed to the function. You can almost think of it as if you are accessing r through a de-referenced pointer (though there is more to it than that).
Another way to look at it: The caller will see changes to r (if it wasn't const), while he will not see changes to S.
The const simply means the function f cannot modify r.
See also: https://isocpp.org/wiki/faq/references#overview-refs

This seems to be just an example, to show the difference between ways of passing parameters.
One real case where you might pass one parameter by value, is when you need a copy of the value anyway. Perhaps when concatenating the two strings.

Related

CPP const function parameters explanation

I have been following this tutorial and I can't understand this section.
So it says that pass by value creates copy of the data so that can be ineffiecient, since it creates copy of the data, makes sense:
string concatenate (string a, string b)
{
return a+b;
}
then you have this, to 'solve the creating copies of data' issue:
string concatenate (string& a, string& b)
{
return a+b;
}
which makes sense since it is using references.
But I do not get this:
string concatenate (const string& a, const string& b)
{
return a+b;
}
Which says:
By qualifying them as const, the function is forbidden to modify the values of neither a nor b, but can actually access their values as references (aliases of the arguments), without having to make actual copies of the strings.
Why just not use the second way? In this example no modification of data is being done anyway? Can somebody give me a better example ?
Is it just done for safety reasons?
If the strings you actually have are const, or you only have const references to them, then you can pass them to this function:
string concatenate (const string& a, const string& b);
But you cannot pass them to this function:
string concatenate (string& a, string& b);
If your strings aren't const, then you can pass them to either function anyway. So other than ensuring that you don't modify the strings unintentionally, the const on the parameter allows for a more versatile function, which can accept either const or non-const arguments.
For example:
string concatenate_non_const (string& a, string& b)
{
return a+b;
}
string concatenate_const (const string& a, const string& b)
{
return a+b;
}
int main()
{
std::string s1 = "hello";
std::string s2 = "world";
const std::string cs1 = "hello";
const std::string cs2 = "world";
concatenate_const(s1, s2); // okay
concatenate_const(cs1, cs2); // okay
concatenate_non_const(s1, s2); // okay
concatenate_non_const(cs1, cs2); // error
}
Also, besides objects which are declared const, there is also temporaries to think about, which cannot be bound to non-const references.
concatenate_const("hello", "world"); // okay
concatenate_non_const("hello", "world"); // error
Why just not use the second way ? In this example no modification of data is being done anyway? Can somebody give me a better example ?
In your case here, where you are writing the function, It really doesn't matter, since you yourself aren't changing the parameters.
A better Example
A better example for using references over const references is for example with a swap function. Take this for example, which swaps 2 ints.
void swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
Now, in the example above, you cannot use a const reference, since the assignment to a and b would qualify as modification. In a function like above, references are very useful, and the const alternative would not work.
A less Concrete Example
Here is a less concrete example, that illustrates the same concepts as the swap(a,b) function:
void pre_incr(int& a)
{
++a;
}
Now the reason why this function is less concrete than the swap() one is that, you could have equally used pass by value like this:
void pre_incr(int a)
{
return ++a;
}
Either way though, both functions illustrate the difference between pass by const reference and pass by normal reference, and also pass by value.
Is it just done for safety reasons ?
Much of what constitutes a programming language are "safety reasons", otherwise we could be jumping around labels in assembly like code.
Making the parameters of your function references to const strings serves two purposes:
You cannot accidentally modify the referenced strings.
It clearly documents "the strings you pass here will not be modified" to both other programmes (or your future self) and the compiler (and optimizer).
Consider this scenario:
string a = "foo", b = "bar";
cout << concatenate(a, b); // "foobar"
You get what you expect, even if you'd modified the passed strings in your concatenation function.
string a = "foo", b = "bar";
cout << concatenate(a, b);
// some code
string c = "baz";
cout << concatenate(a, c); // "Aaaabaz"
You'd probably search all of that "some code" to find modifications to a before even considering to look into concatenate ...
Also note that the first example (single use of the parameter strings) is what you'd likely find in a unit test for concatenate.
When you pass a string in the first method with no const and no & it needs to be copied into the function which is inefficient. When you pass a reference it is not copied. When you pass by const reference it avoids the inefficiency of copying while not allowing the function to modify the string.

Temporary objects in C++

I was going through reference return and came across temporary objects. I don't understand how to identify them. Please explain using this example:
If a and b are objects of same class, consider binary operator+. If you use it in an expression such as f(a+b), then a+b becomes temporary object and f has to of form f(const <class name>&) or f(<class name>). It can't be of form f(<class name>&) However, (a+b).g() is perfectly alright where g() can even change contents of object returned by a+b.
When you say f(a + b), the parameter of f needs to bind to the value with which the function was called, and since that value is an rvalue (being the value of a function call with non-reference return type)*, the parameter type must be a const-lvalue-reference, an rvalue-reference or a non-reference.
By constrast, when you say (a + b).g(), the temporary object is used as the implicit instance argument in the member function call, which does not care about the value category. Mutable values bind to non-const and const member functions, and const values only bind to const member functions (and similarly for volatile).
Actually, C++11 did add a way to qualify the value category of the implicit instance argument, like so:
struct Foo()
{
Foo operator+(Foo const & lhs, Foo const & rhs);
void g() &; // #1, instance must be an lvalue
void g() &&; // #2, instance must be an rvalue
}
Foo a, b;
a.g(); // calls #1
b.g(); // calls #1
(a + b).g(); // calls #2
*) this is the case for an overloaded operator as in this example, and also for built-in binary operators. You can of course make overloaded operators which produce lvalues, though going against the common conventions would probably be considered very confusing.
Your confusion comes not from that you cannot identify temporary objects, in both cases result of a+b is temporary object, but wrong assumption that non const method requires lvalue and would not accept temporary object, which is not true.
For a simple case, think of following piece of code:
int func(int lhs, int rhs)
{
return lhs + rhs;
}
int main() {
int a = 1, b = 2, c = 3;
return func(a * c, b * c);
}
Because func takes two integers, the program must calculate the values of a * c and b * c and store them somewhere -- it can't store them in a or b or c. So the resulting code is equivalent to:
int lhsParam = a * c;
int rhsParam = b * c;
return func(lhsParam, rhsParam);
Again, at the end of func() we return a calculate value, lhs + rhs. The compiler must store it in a new place.
For integers and so forth this seems very simple, but consider instead
int function(std::string filename);
function("hello");
filename has to be a std::string, but you passed a const char*. So what the compiler does is:
std::string filenameParam = "hello"; // construct a new object
function(filenameParam);
just like the previous example, but this time it is hopefully clearer that we're constructing a temporary object.
Note: The convention of calling them "somethingParam" is just for clarity in this answer.

Calling the parenthesis overload given a pointer

I can overload the parenthesis operator using the following signature:
char& operator()(const int r, const int c);
The intended usage of this would be:
// myObj is an object of type MyClass
myObj(2,3) = 'X'
char Y = myObj(2,3);
Which works as I expect. However, using the parenthesis operator when dealing with a pointer becomes convoluted. I would like to do:
// pMyObj is a pointer to an object of type MyClass
pMyObj->(2,3) = 'X';
char Y = pMyObj->(2,3);
However, such syntax yields the error Error: expected a member name (in VisualStudio at least).
The following does work but seems convoluted to me with a dereference and more parentheses than arguments.
char X = (*pMyObj)(2,3);
Is there a way to use the -> operator to call the () overload?
Yes there is, but you won't like it:
pMyObj->operator()(2,3);
You could also create a reference to the pointed to object and do
MyObj& rMyObj = *pMyObj;
char Y = rMyObj(2, 3);
which might be a good alternative if your code will be read by people who could be confused by
pMyObj->operator()(2,3);
if you do as below
#define SUB operator()
Then you can write things like this ...
pMyObj->SUB(2,3)
not as elegant as Fortran ;-) but perhaps not too ugly for actual use

How does ampersand in the return type of a function declaration work? [duplicate]

This question already has answers here:
In C++, what does & mean after a function's return type?
(11 answers)
Closed 10 years ago.
In this piece of code, why f() is declared as "double & f(..."? What does it mean and how does it work? I don't even know what to google to find the answer to my question. Please help.
double a = 1, b = 2;
double & f (double & d) {
d = 4;
return b;
}
I know ampersand sign means the address of a variable or a function, but I don't see why it would make sense to write it when you are declaring a function.
Consider these two functions: power2() and add():
void power2 (double& res, double x) {
res = x * x;
}
double& add (double& x) {
return ++x;
}
The first computes the power of x and stores the result in the first argument, res, – it does not need to return it.
The second returns a reference, which means this reference can later be assigned a new value.
Example:
double res = 0;
power2(res, 5);
printf("%f\n", res);
printf("%f\n", ++add(res));
Output:
25.000000
27.000000
Please note that the second output is 27, not 26 – it's because of the use of ++ inside the printf() call.
When the & operator is used in a declaration form, preceded by a type it doesn't mean "the address of" but a "reference to" which is essentially an automatically dereferenced pointer with disabled pointer arithmetic.
There are no references in C, so if you want to pass or return by reference, you had to pass a const pointer and dereference it to access the pointed to value. C++ added references to make this concept easier, to prevent accidental walking off the address with pointer arithmetic, and to save the need to dereference the pointer. This makes working with it much easier and resulting in cleaner and more readable syntax.
In this case, the ampersand does not mean taking an address, but it denotes a reference. Here, f is a function that takes a reference to double as parameter and returns a reference to double.
You might want to read about C++'s references in your textbook of choice, since they are a very basic part of the language.

C++ pass by ref compilation error

With C++, I struggle to understand one compilation error.
I have this function, with this given signature:
void MethodNMMS::tryNMSA(double factor, double temperature,double& funcWorst,int& iWorst, double& funcTry, double* funcEvals)
{
//...
}
My question concerns argument double& functry (for instance). I call this function tryNMSA() in another function, and I would like functry to be modified during execution of this function. That is why I pass by reference.
Here is the function call:
// other initializations for funcEvals...
double funcTry = 0;
tryNMSA(-1.0,temperature,funcWorst,iWorst,&funcTry,funcEvals);
I put this ampershead because I do want to pass the thing by reference. And that is not permitted. What is wrong, why?
Thanks and regards.
You should not put an ampersand there, as doing so gives a pointer, not a reference to funcTry. Getting a reference to a variable doesn't require any special symbols or operators - just use the name of the variable.
Simply remove the & when you call the function - adding the & means you're trying to pass a pointer.
If you don't put the &, you'll pass a reference. No special syntax necessary.
By passing &funcTry, you are passing the address of funcTry, which would match a function expecting a pointer to double. Your call should simply be
ryNMSA(-1.0,temperature,funcWorst,iWorst,funcTry,funcEvals);
Outside of declarations, a single ampersand means address-of, so &foo means address-of foo.
You should omit the ampersand on the call like this:
tryNMSA(-1.0,temperature,funcWorst,iWorst,funcTry,funcEvals);
The ampersand in the method declaration marks it as a reference argument. When you do ampersand in the method call, you are passing the address of the funcTry variable which is immutable and can't be passed by reference, so compiler gives you an error.
Delete the & in front of the parameter.
Info here:
http://pages.cs.wisc.edu/~hasti/cs368/CppTutorial/NOTES/PARAMS.html
Reference Parameters
When a parameter is passed by reference, conceptually, the actual parameter itself is passed (and just given a new name -- the name of the corresponding formal parameter). Therefore, any changes made to the formal parameter do affect the actual parameter. For example:
void f(int &n) {
n++;
}
int main() {
int x = 2;
f(x);
cout << x;
}
In this example, f's parameter is passed by reference. Therefore, the assignment to n in f is actually changing variable x, so the output of this program is 3.
I won't repeat the other answers. I just wanted to say that this is a recurring issue with learners of C++. The problem is that the & sign has three completely different meanings, and this is not immediately obvious to a beginner:
If x is an l-value, then &x is its address.
If <type> x declares a variable of type <type>, then <type> &x declares a variable of type reference to <type>.
a & b is the bitwise-and operator.
This is similar to the * sign:
If x is a pointer, then *x is its contents.
If <type> x declares a variable of type <type>, then <type> *x declares a variable of type pointer to <type>.
a * b is the multiplication operator.
For some reason, the * operator seems to cause fewer problems than the & operator. Perhaps this is just historical accident: references are newer than pointers.
You can modify a parameter in 2 ways.
Method 1: (reference):
void MethodNMMS::tryNMSA(double& funcTry)
{
funcTry = funcTry + 1.0;
//...
}
// other initializations for funcEvals...
double funcTry = 0;
tryNMSA(funcTry);
Method 2: (pointer):
void MethodNMMS::tryNMSA(double* funcTry)
{
(*funcTry) = (*funcTry) + 1.0;
//...
}
// other initializations for funcEvals...
double funcTry = 0;
tryNMSA(&funcTry);
Make up your mind and use only one of them, it saves a great deal of confusion.
(actually, here you can use one more method - the return value). It's useful to put emphasis on this one value being main purpose of the function.
double MethodNMMS::tryNMSA(double funcTry)
{
//...
return funcTry + 1.0;
}
// other initializations for funcEvals...
double funcTry = 0;
funcTry = tryNMSA(funcTry);