opApply & opApplyReverse Member Functions
Understand foreach support by opApply and opApplyReverse member functions.
We'll cover the following
foreach
, opApply
, and opApplyReverse
Everything that is said about opApply
in this section is valid for opApplyReverse
as well. opApplyReverse
is for defining the behaviors of objects in the foreach_reverse
loops.
The member functions above allow us to use objects as ranges. That method is more suitable when there is only one sensible way of iterating over a range. For example, it would be easy to provide access to individual students of a Students
type.
On the other hand, sometimes it makes more sense to iterate over the same object in different ways. We know this from associative arrays, where it is possible to access either only to the values or to both the keys and the values:
string[string] dictionary; // from English to Turkish
// ...
foreach (inTurkish; dictionary) {
// ... only values ...
}
foreach (inEnglish, inTurkish; dictionary) {
// ... keys and values ...
}
opApply
allows using user-defined types with foreach
in various and sometimes more complex ways. Before learning how to define opApply
, you must first understand how it is called automatically by foreach
.
The program execution alternates between the expressions inside the foreach
block and the expressions inside the opApply()
function. First the opApply()
member function gets called, and then opApply
makes an explicit call to the foreach
block. They alternate in that way until the loop eventually terminates. This process is based on a convention, which we will explain soon.
Let’s first observe the structure of the foreach
loop one more time:
// The loop that is written by the programmer:
foreach (/* loop variables */; myObject) {
// ... expressions inside the foreach block ...
}
If there is an opApply()
member function that matches the loop variables, then the foreach
block becomes a delegate, which is then passed to opApply()
.
Accordingly, the loop above is converted to the following code behind the scenes. The curly brackets that define the body of the delegate are highlighted:
// The code that the compiler generates behind the scenes:
myObject.opApply(delegate int(/* loop variables */) {
// ... expressions inside the foreach block ...
return hasBeenTerminated;
});
In other words, the foreach
loop is replaced by a delegate that is passed to opApply()
. Before showing an example, here are the requirements and expectations of this convention that opApply()
must observe:
-
The body of the
foreach
loop becomes the body of the delegate.opApply
must call this delegate for each iteration. -
The loop variables become the parameters of the delegate.
opApply()
must define these parameters asref
. -
The return type of the delegate is
int
. Accordingly, the compiler injects a return statement at the end of the delegate, which determines whether the loop has been terminated (by abreak
or areturn
statement. If the return value is zero, the iteration must continue; otherwise, it must terminate. -
The actual iteration happens inside
opApply()
. -
opApply()
must return the same value that is returned by the delegate.
The following is a definition of NumberRange
that is implemented according to that convention:
Get hands-on with 1400+ tech skills courses.