函數對象

函數對象（function object）a是一個程序設計的對象允許被當作普通函數來調用。

與之相區分：

- functionoid（函數體對象）定義為「有一個主要方法」的對象（object），不要求是什麼類型，只要能 「調用」就行。例如：`struct LessThan { bool compare(int a, int b) const { return a < b; } };`
- functor（仿函數）定義為：重載了 operator 的類對象（class object）。在 C++ 里，functor 就是 function object。
- lambda對象，生成一個匿名的 closure object，其類型叫 closure type
- std::bind(...) 返回對象，更準確叫bind expression object。
- std::function 對象，更準確叫 type-erased polymorphic function wrapper。通常也算functor。
- C++20 CPO，Customization Point Object，即「定製點對象」。例如`std::ranges::begin`。CPO 的重點不是「可調用」，而是「可定製」。所以 CPO 不只是「一個 functor」，它還承擔了統一定製入口的角色。
- C++20 Niebloid，用於描述 C++20 ranges 中的演算法實體，例如`std::ranges::sort`。Niebloid 的關鍵特徵包括：不能顯式指定模板實參；不參與普通ADL。

函數對象與函數指針相比，有兩個優點：第一是編譯器可以內聯執行函數對象的調用；第二是函數對象內部可以保持狀態。

函數式程序設計語言還支持閉包，例如，first-class函數支持在其創建時用到的函數外定義的變量的值保持下來，成為一個函數閉包。

## C++函數對象的實例

傳統的C/C++函數指針：

```C
#include <stdlib.h>

/* Callback function, returns < 0 if a < b, > 0 if a > b, 0 if a == b */
int compareInts(const void* a, const void* b)
{
  return *(const int *)a - *(const int *)b;
}
...
// prototype of qsort is
// void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
...
int main(void)
{
    int items[] = { 4, 3, 1, 2 };
    qsort(items, sizeof(items) / sizeof(items[0]), sizeof(items[0]), compareInts);
    return 0;
}
```

C++中，函數對象是定義了函數調用運算符的類對象，稱作class type functor。

```Cpp
// comparator predicate: returns true if a < b, false otherwise
struct IntComparator
{
  bool operator()(const int &a, const int &b) const
  {
    return a < b;
  }
};
...
// An overload of std::sort is:
template <class RandomIt, class Compare>
void sort(RandomIt first, RandomIt last, Compare comp);
...
int main()
{
    std::vector<int> items { 4, 3, 1, 2 };
    std::sort(items.begin(), items.end(), IntComparator());
    return 0;
}
```

除了類類型函數對象，還有其他類型的函數對象，如使用成員函數指針或模板。C++11允許使用具有閉包功能的匿名函數。

## C++ STL中的函數對象

C++的STL中的眾多algorithm，非常依賴於函數對象處理容器的元素。因此，STL預定義了許多函數對象、謂詞（predicate）、以及用於複合（composite）函數對象的binder、member function adapter、 pointer to function adapters、 negaters、 function objects base structure。由於STL中的algorithm使用函數對象作為參數時，一般都是傳值調用，所以函數對象應該仔細設計其複製構造函數。

### 預定義的函數對象

C++98在頭文件functional中定義了下述函數對象:

- plus 結果為(param1 + param2)
- minus 結果為(param1 - param2)
- multiplies 結果為(param1 * param2)
- divides 結果為(param1 / param2)
- modulus 結果為(param1 % param2)

### 謂詞（predicate）

返回布爾值（或者可以隱式轉換為布爾值）的函數對象。用於STL中的algorithm時，謂詞應該是無狀態的（stateless）函數對象，即謂詞的結果不依賴於內部的數據成員。這是因為STL中的algorithm不保證內部實現時對傳入的謂詞要複製多少次。

C++98在頭文件functional中定義了下述謂詞:

- equal_to 結果為(param1 == param2)
- not_equal_to 結果為(param1 != param2)
- less 結果為 (param1 < param2)
- greater 結果為(param1 > param2)
- less_equal 結果為 (param1 <= param2)
- greater_equal 結果為 (param1 >= param2)
- logical_not 結果為 (!param1)
- logical_and 結果為 (param1 && param2)
- logical_or 結果為 (param1

param2)

### Function Adapter

用於組合（combine）、變換（transform）、操作（manipulate）函數對象、特定參數值、或者特定函數。進一步細分為：

#### Binder

C++98在頭文件functional中定義了兩個函數bind1st與bind2nd，返回值為binder1st、binder2nd類型。用於把二元函數對象分別綁定第一個、第二個參數後成為單元函數對象。

#### Negater

negate把一個作為謂詞的函數對象取反。C++98在頭文件functional中定義了兩個函數not1與not2，返回值為unary_negate、binary_negate類型。

#### Member function adapter

Member function adapter用於把類的成員函數用作STL中的algorithm的參數。C++98在頭文件functional中定義了:

- 函數mem_fun，返回值為mem_fun_t類型，用於通過一個類對象指針來調用成員函數指針。
- 函數mem_fun_ref，返回值為mem_fun_ref_t類型，用於通過一個類對象引用來調用成員函數指針。

#### Pointer to function adapter

函數指針適配器（Pointer to function adapter）是把函數指針包裝為一個函數對象，以便STL中的algorithm用函數對象作為統一的參數類型，不用再考慮以函數指針作為傳入參數的情形。C++98在頭文件functional中定義了:

- 函數ptr_fun，返回值為pointer_to_unary_function類型，包裝了一個單參數的函數指針。
- 重載函數ptr_fun，返回值為pointer_to_binary_function類型，包裝了一個雙參數的函數指針。

### Function Object Base

函數對象基類（Function Object Base）定義在頭文件functional中，用作STL的預定義的與函數對象有關的各個類的基類，其中定義了幾個類型，分別表示函數調用的各個參數類型、結果類型。

- unary_function類，定義了2個類型：argument_type、result_type。
- binary_function類，定義了3個類型：first_argument_type、second_argument_type、result_type。

## C#

C#的函數對象是通過委託（delegate）聲明的。

```csharp
using System;
using System.Collections.Generic;

static int CompareFunction(int x, int y)
{
    return x - y;
}

List<int> items = new(4, 3, 1, 2);
Comparison<int> del = CompareFunction;
items.Sort(del);
```

```csharp
using System;
using System.Collections.Generic;

List<int> items = new(4, 3, 1, 2);
items.Sort((x, y) => x - y);
```

## JavaScript

JavaScript中，函數是頭等對象，並且支持閉包。

```javascript
function Accumulator(start) {
  let current = start
  return function (x) {
    return current += x
  }
}
```

用法：

```javascript
let a = Accumulator(4)
let x = a(5)   // x has value 9
x = a(2)       // x has value 11

let b = Accumulator(42)
x = b(7)       // x has value 49 (current = 49 in closure b)
x = a(7)       // x has value 18 (current = 18 in closure a)
```

## PowerShell

在Windows PowerShell語言中，腳本塊（script block）是可作為一個整體單元使用的語句集合或表達式集合。腳本塊可以接收參數並返回值。腳本塊是Microsoft .NET Framework類型System.Management.Automation.ScriptBlock的實例。

```powershell
Function Get-Accumulator($x) {
    {
        param($y)
        return $x += $y
    }.GetNewClosure()
}
```

```ps1con
PS C:\> $a = Get-Accumulator 4
PS C:\> & $a 5
9
PS C:\> & $a 2
11
PS C:\> $b = Get-Accumulator 32
PS C:\> & $b 10
42
```

## Python

Python程序設計中，函數是作為頭等對象（first-class object），可以如同普通的字串、數值、list等對象那樣操作。這使得大部分編寫函數對象都是不必須的。

任何對象定義了`__call__()`方法，就具有了可以當作函數調用的語法。例如下面的做累加的類

```Python
class Accumulator(object):
    def __init__(self, n):
        self.n = n
    def __call__(self, x):
        self.n += x
        return self.n
```

一個使用例子：

```pycon
 >>> a = Accumulator(4)
 >>> a(5)
 9
 >>> a(2)
 11
 >>> b = Accumulator(42)
 >>> b(7)
 49
```

因為函數就是對象，可當作局部變量定義、確定其屬性（內部狀態）、作為別的函數的返回值。例如:

```Python
def Accumulator(n):
    def inc(x):
        inc.n += x
        return inc.n
    inc.n = n
    return inc
```

Python 3中，可以用函數閉包來創建函數對象：

```Python
def Accumulator(n):
    def inc(x):
        nonlocal n
        n += x
        return n
    return inc
```

## 進一步閱讀

- David Vandevoorde & Nicolai M Josuttis (2006). C++ Templates: The Complete Guide, ISBN 0-201-73484-2: Specifically, chapter 22 is devoted to function objects.

## 外部連結

- Function Objects (STL) by Gabriel&Andreas Fleseriu&Masur on February 22nd, 2006
- Description from the Portland Pattern Repository
- C++ Advanced Design Issues - Asynchronous C++ by Kevlin Henney
- The Function Pointer Tutorials by Lars Haendel (2000/2001)
- Article " Generalized Function Pointers" by Herb Sutter
- Generic Algorithms for Java
- PHP Functors - Function Objects in PHP
- What the heck is a functionoid, and why would I use one? (C++ FAQ)