### Search

All of the overloaded operators you have seen so far let you define the type of the operator’s parameters, but not the number of parameters (which is fixed based on the type of the operator). For example, operator== always takes two parameters, whereas operator! always takes one. The parenthesis operator (operator()) is a particularly interesting operator in that it allows you to vary both the type AND number of parameters it takes.

There are two things to keep in mind: first, the parenthesis operator must be implemented as a member function. Second, in non-object-oriented C++, the () operator is used to call functions. In the case of classes, operator() is just a normal operator that calls a function (named operator()) like any other overloaded operator.

An example

Let’s take a look at an example that lends itself to overloading this operator:

Matrices are a key component of linear algebra, and are often used to do geometric modeling and 3D computer graphics work. In this case, all you need to recognize is that the Matrix class is a 4 by 4 two-dimensional array of doubles.

In the lesson on overloading the subscript operator, you learned that we could overload operator[] to provide direct access to a private one-dimensional array. However, in this case, we want access to a private two-dimensional array. Because operator[] is limited to a single parameter, it is not sufficient to let us index a two-dimensional array.

However, because the () operator can take as many parameters as we want it to have, we can declare a version of operator() that takes two integer index parameters, and use it to access our two-dimensional array. Here is an example of this:

Now we can declare a Matrix and access its elements like this:

which produces the result:

```4.5
```

Now, let’s overload the () operator again, this time in a way that takes no parameters at all:

And here’s our new example:

which produces the result:

```0
```

Because the () operator is so flexible, it can be tempting to use it for many different purposes. However, this is strongly discouraged, since the () symbol does not really give any indication of what the operator is doing. In our example above, it would be better to have written the erase functionality as a function called clear() or erase(), as `matrix.erase()` is easier to understand than `matrix()` (which could do anything!).

Having fun with functors

Operator() is also commonly overloaded to implement functors (or function object), which are classes that operate like functions. The advantage of a functor over a normal function is that functors can store data in member variables (since they are classes).

Here’s a simple functor:

Note that using our Accumulator looks just like making a normal function call, but our Accumulator object is storing an accumulated value.

You may wonder why we couldn’t do the same thing with a normal function and a static local variable to preserve data between function calls. We could, but because functions only have one global instance, we’d be limited to using it for one thing at a time. With functors, we can instantiate as many separate functor objects as we need and use them all simultaneously.

Conclusion

Operator() is sometimes overloaded with two parameters to index multidimensional arrays, or to retrieve a subset of a one dimensional array (with the two parameters defining the subset to return). Anything else is probably better written as a member function with a more descriptive name.

Operator() is also often overloaded to create functors. Although simple functors (such as the example above) are fairly easily understood, functors are typically used in more advanced programming topics, and deserve their own lesson.

Quiz time

Question #1

Write a class that holds a string. Overload operator() to return the substring that starts at the index of the first parameter. The length of the substring should be defined by the second parameter.

Hint: You can use array indices to access individual chars within the std::string
Hint: You can use operator+= to append something to a string

The following code should run:

This should print

```world
```

Show Solution

• Tony

Oh? Am I the only one not seeing any comment anymore?

• nascardriver

I thought it was just a display problem, but it appears the comments are no longer linked to the lessons they were posted in. I'll leave to this Alex to figure out.

• Tony Kolarek

Yep, it's fixed now!

• Tony Kolarek

Yoo Nascar, what's up!

I've done the quiz differently. Is this ok?

I did this (I forgot of assert indeed):

• CC

Here's an implementation of the overload in the quiz using `std::string_view`, which I imagine is more efficient than the one suggested in the solution. (Though probably not better than `std::string::substr`.)

Of course, I still need to pass the view by value rather than reference, but I imagine it is better than growing a string character-by-character and then copying it to the caller.

• nascardriver

Yes it's more efficient. `std::string_view` should not be passed by reference anyway. It's designed to be fast when passed by value.

• SaMIRa

Hello nascardriver,
I was wondering if the following solution is both correct and efficient for overloading 'operator[]' for a two-dimentional array?

Output:
10 11 12
13 14 15
16 17 18
19 20 21

• nascardriver

Correct and efficient. Improvements:

- Avoid all-caps names, they might collide with macros
- Use array syntax to access arrays
- `OneDimArray` stores a pointer to an array (ie. `double**`). That's not necessary at leads to more complex syntax and is harder to understand. `ArrayType` can be a `double*`.
- You don't need `counter`. Using `column` in its place has the same effect. You can also just nest 2 for loops so that you don't have to iterator over 2 variables in a single loop.
- Use single quotes for characters. " " is a string, ' ' is a character.
- Use ++prefix unless you need postfix++
- If a loop's body exceeds 1 line, wrap it in curly braces

• SaMIRa

>>That's not necessary at leads to more complex ...
I didn't get this part. Did you meant 'as it leads'?

I was wondering if we wanted to implement operator[][][][] for 4-D arrays, then would we have to define three different classes for each dimensions? Is there any way in C++ to be able to do that with just one class? I Googled it and I found out 'C++ Proxy' could be helpful. What do you think?

Does this look good? (Thanks for always-great tips)

• nascardriver

"That's not necessary [and] leads [...]"

You can implement n-dimensional arrays using templates. I suggest you finish the tutorials and get some practice before having a go at this.

I still find you `print` hard to follow. It's much easier with 2 loops.

• SaMIRa

>>I suggest you finish the tutorials and get some practice before having a go at this.
Sure. of course. Thanks.

>>I still find you `print` hard to follow. It's much easier with 2 loops
I will modify that too. I thought maybe one loop would be efficient than nested loops. But it turns out it is not. Thank you again for all of your time and help @)-,--.

• operator[] for matrices

I would like to implement operator[] for matrices, but I want to make sure with what we have learned so far about C++, can we overload operator[] for matrices? I mean do we need to know some more concepts of C++ or is it enough to implement it?

• Gamer_to_be

shouldn't we have 'm_data' instead of 'data' for the sake of consistency :)

• nascardriver

Lesson updated, thanks!

• Rushhahb

Can we overload operator[] for a member variable which is 'vector' not 'array'?

• nascardriver

You can overload `operator[]` for everything

• Burak

Hello Nascardriver,

first of all, while(1) { std::cout << "Many thanks for this website!" << '\n' }. I've got one comment. If you put in a negative value for the start index, a programm with the solution crashes. Wouldn't be wise to add an assertion for this case as well or extend the already available assertion?

Best regards

Burak

• nascardriver

Makes sense, thanks!

• Tim

I want to say thank you again to Alex and Nascardriver for this amazing website!

I have a question though about the return types of the overloaded operators in the Matrix class.

I understand the return type double& for non-const objects, since that allows retrieving as well as modifying member data.

What I don't get is why for const objects, the return type is const double&. Shouldn't this be just double, since returning fundamental types by value is more efficient and returning a copy is enough anyway (since member data should not be modifiable for const objects)?

• nascardriver

It should just be `double`, not `const double&`. Thanks!

• Vikrant Ravindr Waje

Why does the following code prints nothing??
The solution is for the Challenge question :

• nascardriver

Line 26. There is no `temp[count]`, `temp` is empty. Either initialize `temp` with its final length

or append to temp

Don't use postfix++ unless you need to. ++prefix is faster.

Why does this code does not compile?

Code:Blocks throws me an error at

C:\Users\Xenon\Desktop\Stuff\Programming\C++\Test\main.cpp||In member function 'std::__cxx11::string String::operator()(int, int) const':|
C:\Users\Xenon\Desktop\Stuff\Programming\C++\Test\main.cpp|16|error: no matching function for call to 'std::__cxx11::basic_string<char>::append(const value_type&)'|
C:\Users\Xenon\Desktop\Stuff\Programming\C++\Test\main.cpp|16|note:   candidate expects 3 arguments, 1 provided|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

However if I switch 'append' member function with += operator, it runs perfectly.
Why does this happen?

• nascardriver

There is no overload of `append` that takes a char, see https://en.cppreference.com/w/cpp/string/basic_string/append
Using += is the easiest solution.

• ruchika malhotra

Hi,

In the quiz if I remove the const I am getting an error. Can you explain why?

main.cpp: In function ‘int main()’:
main.cpp:45:38: error: cannot bind non-const lvalue reference of type ‘std::string& {aka std::basic_string&}’ to an rvalue of type ‘std::string {aka std::basic_string}’
Mystring string{ "Hello, world!" };

• nascardriver

"Hello, world!" is an r-value. Non-const references cannot bind to r-values, because that would mean they could modify the value.

• ruchika malhotra

yaaa .I totally forgot. Thank you !
Also , I got confused now,

class Something
{
private:
int m_value1;
double m_value2;
char m_value3;

public:
Something(int value1, double value2, char value3='c')
: m_value1{ value1 }, m_value2{ value2 }, m_value3{ value3 } // directly initialize our member variables
{
// No need for assignment here
}

void print()
{
std::cout << "Something(" << m_value1 << ", " << m_value2 << ", " << m_value3 << ")\n";
}

};

int main()
{
Something something{ 1, 2.2 }; // value1 = 1, value2=2.2, value3 gets default value 'c'
something.print();
return 0;
}

Here 1,2.2 are also R values ,then why we are not using const here?

• ruchika malhotra

Ohh , Here we are not using by reference that's why .

• hugo

Just wondering why we invent our own wheels instead of using the robust substr() member function provided by string library:

• nascardriver

In actual code, you shouldn't reinvent the wheel but use the standard library as much as possible. But the lessons are here to teach, and it's good to know how something works. That's best learned by implementing it.
I linked to std::string::substr in the quiz's solution. If you find any more quizzes where there's an alternative standard implementation but no link yet, feel free to point it and so we can add a link.

• no boots

Hi nascardriver and Alex!

Wouldn't overloading the subscript operator this way also let you index multidimensional arrays? Or could this be something I should avoid doing?
I made it so that the subscript operator returns a pointer to the first element of an array and it works fine.

• nascardriver

You can't prevent the user from accessing invalid indexes that way, because you gave them full access to the array. If you trust the user of your function, that's not a problem.

• Ged

Why are you using "static_cast<std::string::size_type>(start + count)", cause my line seems much more simpler.

• nascardriver

`std::string::operator[]` wants a `std::string::size_type`. If you pass it an `int` and your compiler is configured to warn about unsigned conversions, you'll get a warning.

• sito

Oh i see. So basically the []operator when used with std::string wants a std::string size_type in order to work properly. I now have 2 more questions in order to understand this. First off why didn't my compiler warn me about this? using visual studio2019. Could you instead of having to cast the int parameters just make them as unsigned ints? and then make a loop with an unsigned int variable? it would be one cast less you would have to make in the loop or am I thinking about this the wrong way?

• nascardriver

/W4 doesn't enable C4365, which is the warning responsible for signed/unsigned conversions. /Wall includes it, but might enable more warnings than necessary. /w44365 enables this specific warning when you're using /W4.

If you're going to change the loop variable's type, use the correct one right away, `std::string::size_type`. Keep in mind that this is unsigned, a reverse loop, you can't go negative.

• sito

hello! Why do you make the code in the sullution for quiz 1 so complecated? in my opinion it's more complecated than it needs to be. Wouldn't it be better to do it like this? see code below. There is one cast  and it's more readable than your example or are there bennefits to doing it your way?

I might be missing something here but I think this is such a short program where the string isn't long enough to really care about performence in this case.

• nascardriver

You have a signed to unsigned conversion in your code. Your compiler should have warned you.

Apart from you allowing substrings that end after the end of the string, there's no real difference in the code. What makes you think one or the other is easier to read?

• sito

hello! this might just be because I haven't done my homework correctly or that i've simply forgot  but there are 2 parts I don't understand, first off what does this part do  at the end of the assert statement
&& "Mystring::operator(int, int): Substring is out of range");?
Also why do you use size type in the loop? again this might be simple questions and I have just not done my homework correctly if that's the case I appologise.

• nascardriver

An assert fails if the condition evaluates to `false`. If an assert fails, you have to understand why it failed. A message helps. We can add a message by adding `&& "message"`, because it doesn't change the condition. A string literal is `true` (Because it's non-zero).
condition && message = x
false && true = false
true && true = true
The message doesn't change the condition.

`operator[]` of `std::string` wants a `std::string::size_type` (An unsigned integer type). If you give it a signed int, the compiler complains about an implicit signed to unsigned conversion (Because this could cause an underflow). The cast silences the warning.

• nascardriver

Sounds like you don't have a non-const `operator()`. The example code is correct.

• Ryan

For the quiz, couldn't you also use:
ret += m_string[static_cast<unsigned int>(start++)];

Performance and efficiency wise, which version is better?

• nascardriver

There are several ways of doing this. The compiler should produce the same (optimal) code for every way. Use whichever you find easier to read.

• Daniel

Hey,
I am just here to note, that example solution doesn't work in case you use, Wall and Werror, since std::string operator[](), takes in unsigned int. If you want it to work, you must use static cast/unsigned ints. It can also be 'resolved' by just removing Werror, from your settinsg.
This whole paragraph also applies to assertion line.

However it might be good idea to note that somewhere in the text or update the example, since in first lectures, you recommend to use Werror Wall etc. (It took me more time than I want to admit to find the bug :D)

• Daniel

btw the code is the updated code, so it works properly even with Werror

• nascardriver

Lesson updated, thanks for pointing out the error!
Note that `std::string::length` doesn't necessarily return an `unsigned int`. It returns `std::string::size_type`, which may or may not be `unsigned int`.

• hellmet

Ahh, I see where this can be useful!

Say I have a matrix that has n dimensions, each with size k1, k2, … kn
By combining the () with the ... (ellipsis), one can use it to index into the array of n dimensions!

Perhaps, that is what numpy does?

• Daniel

Hey,
I don't think that is what numpy does, since C++ doesn't have typechecked ellipsis (something like fce(double ...),), you also don't know, how much arguments are passed.
However I think there is solution for these problems throught templates, although I am just at this lecture, so I don't recall correctly, how it was done. However NascarDriver or Alex, might confirm, that this either might be solution or rather might not and they really use ellipsis

• nascardriver

I don't know how NumPy does it. C++ has parameter packs (Basically type-safe variadic parameters), through which this should be possible in a safe way. Variadic arguments shouldn't be used anymore.

• Arunreddy

matrix(1, 2) = 4.5;
how this sets value, i didn't understand.
Thanks.

• It calls `Matrix::operator()(int row, int col)`, which returns a `double&`. If you forgot about reference, re-read lesson 6.11 and 7.4a.

• George Pitchurov

I have hard time figuring out why something really odd. Consider this code:

If you print that, you'll see that first element of strA and strB are 'H', but have different adresses (not surprising). Then however the second element of strA has the SAME adress as the first element of strB, but has different value: 'e' (which is also shared by the second element of strB). Then the third element of strA has the SAME adress as second element of strB, but again has different value 'l' (instead of 'e', which the second strB element has). And the fourth element of strA has the same adress as the third element of strB, but again different value.

So how is possible that one and the same adress holds different values for two variables of the same type? No assignement is done within the loop, so that to expect any overwrite action, only output statements! How is at all possible one and same adress to be allocated to two different array variables that are not reference to each other?

• Line 9: You can't store more than 1 character in a char.

You're taking the address of `strA` and `strB`, which are `std::string`. This address isn't the address of the actual string. Pointer arithmetic on `std::string` uses `std::string`'s size. What you want is

`c_str` returns the C-style string.