函數對象(function object)[note 1]是一個程序設計的對象允許被當作普通函數來調用。

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

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

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

// 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<type>() 結果為(param1 + param2) minus<type>() 結果為(param1 - param2) multiplies<type>() 結果為(param1 * param2) divides<type>() 結果為(param1 / param2) modulus<type>() 結果為(param1 % param2)

謂詞(predicate)

編輯

返回布爾值(或者可以隱式轉換為布爾值)的函數對象。用於STL中的algorithm時,謂詞應該是無狀態的( stateless)函數對象,即謂詞的結果不依賴於內部的數據成員。這是因為STL中的algorithm不保證內部實現時對傳入的謂詞要複製多少次。 C++98在頭文件functional中定義了下述謂詞:

  • equal_to<type>() 結果為(param1 == param2)
  • not_equal_to<type>() 結果為(param1 != param2)
  • less<type>() 結果為 (param1 < param2)
  • greater<type>() 結果為(param1 > param2)
  • less_equal<type>() 結果為 (param1 <= param2)
  • greater_equal<type>() 結果為 (param1 >= param2)
  • logical_not<type>() 結果為 (!param1)
  • logical_and<type>() 結果為 (param1 && param2)
  • logical_or<type>() 結果為 (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;

Python

編輯

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

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

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

一個使用例子:

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

因為函數就是對象,可當作局部變量定義、確定其屬性(內部狀態)、作為別的函數的返回值。[3]例如:

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

Python 3中,可以用函數閉包來創建函數對象:

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

注釋

編輯
  1. ^ In C++, a functionoid is an object that has one major method, and a functor is a special case of a functionoid.[1] They are similar to a function object, but not the same.

參考文獻

編輯
  1. ^ 33.15: What's the difference between a functionoid and a functor? 網際網路檔案館存檔,存檔日期2004-10-13.
  2. ^ Accumulator Generator. [2013-08-03]. (原始內容存檔於2020-11-09). 
  3. ^ Python reference manual - Function definitions. [2013-08-03]. (原始內容存檔於2020-12-15). 

進一步閱讀

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

外部連結

編輯