14.8 Function-Call Operator: Object-Oriented Programming Language 12/10/2014
14.8 Function-Call Operator: Object-Oriented Programming Language 12/10/2014
12/10/2014
14.8 Function-Call Operator
Classes that overload the call operator () allow objects of its type to be used as if they were a
function. These objects are called function object or callable object.
Recall what we have learned in Chapter 10, we can pass any kind of callable object to an STL
algorithm. An object or expression is callable if we can apply the call operator ( 1.5.2, p. 23)
to it. That is, if e is a callable expression, we can write e(args) where args is a commaseparated list of zero or more arguments.
There are three kinds of callables in C++: functions and function pointers ( 6.7, p. 247),
lambda expressions ( 10.3.2), and classes that overload the function-call operator (
14.8, p. 571) which well cover now.
Example: write a simple class AbsInt that takes an integer and return its absolute value. For
example, when we do
cout << a(-3);
We will expect the value of 3. We can of course write a simple function to solve the problem:
absolute.cpp
#include <iostream>
using namespace std;
int absoluteValue(int val){
return val<0 ? -val : val;
}
int main(){
int val;
cout << "Enter a value: ";
cin >> val;
cout << "The absolute value of " << val << " is: "
<< absoluteValue(val) << endl;
return 0;
}
<iostream>
<string>
<vector>
<algorithm>
In this example, we use capture list in lambda expression to do processing that requires more
arguments than the algorithms predicate allows. We can achieve the same goal with functionobject class.
In-class Exercise 14.3: Write a function-object class LongerString to replace lambda
expressions.
Ex14-3.cpp, LongerString.h
#include
#include
#include
#include
#include
#include
<iostream>
<string>
<list>
<vector>
<algorithm>
LongerString.h // function-object class
A:
#ifndef LONGERSTRING_H
#define LONGERSTRING_H
#include <string>
class LongerString
{
public:
LongerString(unsigned i):sz(i){}
3
Remark:
auto end_part = stable_partition(words.begin(),words.end(),
LongerString(wordSize));
cout << "The elements of the map with the customized order are:
<< endl;
for (auto e : m) cout << e.first << " " << e.second << endl;
cout << endl;
return 0;
In-class Exercise 14.4: The following program allows users to enter a few strings and report
them in a descending alphabetic order. The program then uses this information to organize
these strings in an acceding length order. Below is a sample run:
#include <iostream>
#include <string>
#include <set>
using namespace std;
class descendComp {
public:
bool operator() (const string& lhs, const string& rhs) const
{
return lhs > rhs;
}
};
// Your codes (function-class codes)
return 0;
}
Answer:
class lengthComp {
public:
bool operator() (const string& lhs, const string& rhs) const
{
return lhs.size() < rhs.size();
}
};
multiset<string, lengthComp> lcoll (dcoll.begin(),
dcoll.end());
cout << "The strings in an acceding length order are: ";
for (auto e : lcoll) cout << e << " ";
cout << endl;
These types are defined in the <functional> header. For example, the plus class has a
function-call operator that applies + to a pair of operands; the modulus class defines a call
operator that applies the binary operator; the equal_to class applies == and so on.
Quick Concept Example: what are the outputs?
funObj.cpp
#include <iostream>
#include <functional>
using namespace std;
int main(){
plus<int> intAdd; // function object that can add two int values
negate<int> intNegate; // function object that can negate an int
value
cout << intAdd(10, 20) << endl;
cout << intNegate(intAdd(10, 20)) << endl;
cout << intAdd(10, intNegate(10)) << endl;
return 0;
}
A:
30
-30
0
These library defined function objects are thus building blocks of STL (see ppt).
8
The first version applies a unary operation to each element in the input range. The second
applies a binary operation to elements from the two input sequences. Write a program to read
in a few integers from input and output a sequence that negates all the elements and a
sequence that squares all the elements. Below is a sample run:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main(){
cout << "Enter a few integers: ";
vector<int> coll;
int val;
while (cin >> val)
coll.push_back(val);
// your codes transform ...
cout << "negated: ";
for (auto e : coll) cout << e << " ";
cout << endl;
// your codes transform ...
cout << "square: ";
for (auto e : coll) cout << e << " ";
cout << endl;
return 0;
}
A:
transform(coll.begin(), coll.end(), coll.begin(), negate<int>());
This call to bind has only one placeholder, which means that check6 takes a single
argument. The placeholder appears first in arg_list, which means that the parameter in
check6 corresponds to the first parameter of check_size. That parameter is a const
string&, which means that the parameter in check6 is also a const string&. Thus, a
call to check6 must pass an argument of type string, which check6 will pass as the first
argument to check_size.
The second argument in arg_list (i.e., the third argument to bind) is the value 6. That
10
Function adaptors can be used with the STL predefined function object. For example,
auto f = bind(multiplies<int>(), _1, 10);
cout << f(99) << endl; // 990
In the above example, we define a function object that multiplies a first passed argument with
10.
Function adaptor is often used together with STL algorithms to provide a versatile usage. For
example,
bindReplaceif.cpp
#include
#include
#include
#include
<iostream>
<vector>
<algorithm>
<functional>
(Quick Concept Check) More bind example: what are the outputs?
bindDivide.cpp
#include <iostream>
11
return 0;
A:
5
5
0.2
In-class Exercise 14.6: Using library function objects and adaptors to remove all the elements
with values between 5 and 8 for the following elements in a vector:
vector<int> coll = {1, 8, 5, 2, 5, 6, 3, 7, 4, 1, 6, 8, 9, 1,
10};
#include
#include
#include
#include
<iostream>
<vector>
<algorithm>
<functional>
A:
coll.erase(remove_if(coll.begin(),coll.end(),
bind(logical_and<bool>(),
bind(greater_equal<int>(),_1,5),
bind(less_equal<int>(),_1,8))),
coll.end());
13