在呼叫函式時的 ()
是呼叫運算子(call operator),你可以重載呼叫運算子。例如:
#include <iostream>
using namespace std;
struct IntPlus {
int operator()(int rhs, int lhs) const {
return rhs + lhs;
}
};
int main() {
IntPlus plus;
cout << plus(10, 20) << endl;
return 0;
}
在範例中,p
稱為函式物件(function object),又稱為函子(functor),是 Callable 類型,可以像函式一樣地呼叫,範例中的 plus
可以指定給 function<int(int, int)>
型態的變數。
這邊的 IntPlus
實例,相當於 lambda 運算式,[] (int rhs, int lhs) { return rhs + lhs; }
,lambda 運算式多半編譯為匿名的函子,如果一個 lamdbda 運算式有捕捉變數呢?例如 [a] (int b) { return a + b; }
,那麼相當於底下的函子:
#include <iostream>
using namespace std;
int main() {
class __anonymous {
int a;
public:
__anonymous(int a) : a(a) {}
int operator()(int b) const {
return a + b;
}
};
int a = 10;
__anonymous f(a);
cout << f(20) << endl;
return 0;
}
如果一個 lamdbda 運算式以參考方式捕捉變數呢?例如 [&a] { a = 30; }
,那麼相當於底下的函子:
#include <iostream>
using namespace std;
int main() {
class __anonymous {
int &a;
public:
__anonymous(int &a) : a(a) {}
void operator()() const {
a = 30;
}
};
int a = 10;
__anonymous f(a);
f();
cout << a << endl; // 30
return 0;
}
既然如此,不如就使用 lamdbda 運算式就好了,還需要函子嗎?一種說法因為編譯器會對其最佳化,函子比較有效率,不過就目的來說,因為函子是個物件,它就可以攜帶更多的資訊,例如:
#include <iostream>
using namespace std;
class PrintLine {
string sep;
public:
PrintLine(string sep) : sep(sep) {}
void operator()(string text) const {
cout << text << sep;
}
};
int main() {
PrintLine printLf("\n");
PrintLine printCrLf("\r\n");
printLf("print lf");
printCrLf("print crlf");
return 0;
}
還有一個好處是函子可以模版化,在〈高階函式〉中看過,functional
中包含了對應於運算子的函子(Functor),像是 plus
、minus
、multiplies
等,這些函子都模版化了,其中的範例就看過,建立函式物件時就可以指定型態:
#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;
int main() {
auto add10 = bind(plus<int>{}, _1, 10);
auto mul5 = bind(multiplies<int>{}, _1, 5);
cout << add10(30) << endl; // 40
cout << mul5(20) << endl; // 100
return 0;
}