Boost.Bind用法詳解(一)

Boost.Bind 爲函數和函數對象提供了一致的語法,對於值語義和指針語義也一樣。我們將從一些簡單的例子開始,處理一些簡單綁定的用法,然後再轉移到通過嵌套綁定進行函數組合。弄明白如何使用 bind 的關鍵是,佔位符的概念。佔位符用於表示提供給結果函數對象的參數,Boost.Bind 支持最多九個參數。佔位符被命名爲 _1_2_3_4, 直至 _9, 你要把它們放在你原先放參數的地方。作爲第一個例子,我們定義一個函數,nine_arguments, 它將被一個 bind 表達式調用。

#include <iostream>
#include "boost/bind.hpp"

void nine_arguments(
  int i1,int i2,int i3,int i4,
  int i5,int i6,int i7,int i8, int i9) {
  std::cout << i1 << i2 << i3 << i4 << i5
    << i6 << i7 << i8 << i9 << '\n';
}

int main() {
  int i1=1,i2=2,i3=3,i4=4,i5=5,i6=6,i7=7,i8=8,i9=9;
  (boost::bind(&nine_arguments,_9,_2,_1,_6,_3,_8,_4,_5,_7))
    (i1,i2,i3,i4,i5,i6,i7,i8,i9);
}

在這個例子中,你創建了一個匿名臨時綁定器,並立即把參數傳遞給它的調用操作符來調用它。如你所見,佔位符的順序是被攪亂的,這說明參數的順序被重新安排了。注意,佔位符可以在一個表達式中被多次使用。這個程序的輸出如下。

921638457

這表示了佔位符對應於它的數字所示位置的參數,即 _1 被第一個參數替換,_2 被第二個參數替換,等等。接下來,你將看到如何調用一個類的成員函數。

調用成員函數

我們來看一下如何用 bind 調用成員函數。我們先來做一些可以用標準庫來做的事情,這樣可以對比一下用 Boost.Bind 的方法。保存某種類型的元素在一個標準庫容器中,一個常見的需要是對某些或全部元素調用一個成員函數。這可以用一個循環來完成,通常也正是這樣做的,但還有更好的方法。考慮下面這個簡單的類,status, 我們將用它來示範 Boost.Bind 的易用性和強大的功能。

class status {
  std::string name_;
  bool ok_;
public:
  status(const std::string& name):name_(name),ok_(true) {}

  void break_it() {
    ok_=false;
  }

  bool is_broken() const {
    return ok_;
  }

  void report() const {
    std::cout << name_ << " is " <<
      (ok_ ? "working nominally":"terribly broken") << '\n';
  }
};

如果我們把這個類的實例保存在一個 vector, 並且我們需要調用成員函數 report, 我們可能會象下面這樣做。

std::vector<status> statuses;
statuses.push_back(status("status 1"));
statuses.push_back(status("status 2"));
statuses.push_back(status("status 3"));
statuses.push_back(status("status 4"));

statuses[1].break_it();
statuses[2].break_it();

for (std::vector<status>::iterator it=statuses.begin();
  it!=statuses.end();++it) {
  it->report();
}

這個循環正確地完成了任務,但它是冗長、低效的(由於要多次調用 statuses.end()),並且不象使用標準庫算法 for_each 那樣清楚地表明意圖。爲了用 for_each 來替換這個循環,我們需要用一個適配器來對 vector 元素調用成員函數 report 。這時,由於元素是以值的方式保存的,我們需要的是適配器 mem_fun_ref.

std::for_each(
  statuses.begin(),
  statuses.end(),
  std::mem_fun_ref(&status::report));

這是一個正確、合理的方法,它非常簡潔,非常清楚這段代碼是幹什麼的。以下是使用Boost.Bind 完成相同任務的代碼。[1]

[1] 要注意的是boost::mem_fn, 它也被接納進入Library Technical Report, 它也可以在這種沒有參數的情況下使用。 mem_fn 取代了 std::mem_fun 和 std::mem_fun_ref.

std::for_each(
  statuses.begin(),
  statuses.end(),
  boost::bind(&status::report,_1));

這個版本同樣的清楚、明白。這是前面所說的佔位符的第一個真正的使用,我們同時告訴編譯器和代碼的讀者,_1 用於替換這個函數所調用的綁定器的第一個實際參數。雖然這段代碼節省了幾個字符,但在這種情況下標準庫的 mem_fun_ref 和 bind 之間並沒有太大的不同,但是讓我們來重用這個例子並把容器改爲存儲指針。

std::vector<status*> p_statuses;
p_statuses.push_back(new status("status 1"));
p_statuses.push_back(new status("status 2"));
p_statuses.push_back(new status("status 3"));
p_statuses.push_back(new status("status 4"));

p_statuses[1]->break_it();
p_statuses[2]->break_it();

我們還可以使用標準庫,但不能再用 mem_fun_ref. 我們需要的是適配器 mem_fun, 它被認爲有點用詞不當,但它的確正確完成了需要做的工作。

std::for_each(
  p_statuses.begin(),
  p_statuses.end(),
  std::mem_fun(&status::report));

雖然這也可以工作,但語法變了,即使我們想做的事情非常相似。如果語法可以與第一個例子相同,那就更好了,所以我們所關心的是代碼要做什麼,而不是如何去做。使用bind, 我們就無須關心我們處理的元素是指針了(這一點已經在容器類型的聲明中表明瞭,對於現代的庫來說,這樣的冗餘信息是不需要的)。

std::for_each(
  p_statuses.begin(),
  p_statuses.end(),
  boost::bind(&status::report,_1));

如你所見,這與我們前一個例子完全一樣,這意味着如果我們之前已經明白了 bind ,那麼我們現在也清楚它。現在,我們已決定換用指針了,我們要面對另一個問題,即生存期控制。我們必須手工釋放 p_statuses 中的元素,這很容易出錯,也無須如此。所以,我們可能決定開始使用智能指針,並(再次)修改我們的代碼。

std::vector<boost::shared_ptr<status> > s_statuses;
s_statuses.push_back(
  boost::shared_ptr<status>(new status("status 1")));
s_statuses.push_back(
  boost::shared_ptr<status>(new status("status 2")));
s_statuses.push_back(
  boost::shared_ptr<status>(new status("status 3")));
s_statuses.push_back(
  boost::shared_ptr<status>(new status("status 4")));
s_statuses[1]->break_it();
s_statuses[2]->break_it();

現在,我們要用標準庫中的哪個適配器呢?mem_fun 和 mem_fun_ref 都不適用,因爲智能指針沒有一個名爲 report 的成員函數,所以以下代碼編譯失敗。

std::for_each(
  s_statuses.begin(),
  s_statuses.end(),
  std::mem_fun(&status::report));

不巧,標準庫不能幫我們完成這個任務[2]。因此,我們不得不採用我們正想要擺脫的循環,或者使用 Boost.Bind, 它不會抱怨任何事情,而且正確地完成我們想要的。

[2] 以後將可以這樣做,因爲 mem_fn 和 bind 都將成爲未來的標準庫的一部分。

std::for_each(
  s_statuses.begin(),
  s_statuses.end(),
  boost::bind(&status::report,_1));

再一次,這段代碼與前面的例子完全一樣(除了容器的名字不同)。使用綁定的語法是一致的,不論是用於值語義或是指針語義,甚至是用於智能指針。有時,使用不同的語法有助於理解代碼,但在這裏,不是這樣的,我們的任務是對容器中的元素調用成員函數,沒有更多的也沒有更少的事情。語法一致的價值不應被低估,因爲它對於編寫代碼的人,以及對於日後需要維護代碼的人都是有幫助的(當然,我們並不真的是在寫需要維護的代碼,但爲了這個主題,讓我們假裝是在寫)。

這些例子示範了一個非常基本和常見的情形,在這種情形下 Boost.Bind 尤爲出色。即使標準庫也提供了完成相同工作的一些基本工具,但我們還是看到 Bind 既提供了一致的語法,也增加了標準庫目前缺少的功能。

看一下門簾的後面

在你開始使用 Boost.Bind 後,這是無可避免的;你將開始驚訝它到底是如何工作的。這看起來就象是魔術,bind 可以推斷出參數的類型和返回類型,它又是如何處理佔位符的呢?我們將快速地看一下驅動這個東西的機制。它有助於知道一點 bind的工作原理,特別是在試圖解釋這驚人的簡潔性以及編譯器對最輕微的錯誤給出的直接的錯誤信息。我們將創建一個非常簡單的綁定器,至少是部分地模仿 Boost.Bind 的語法。爲了避免把這個離題的討論搞成幾頁那麼長,我們只支持一類綁定,即接受單個參數的成員函數。此外,我們不會對cv限定符進行處理;我們只處理最簡單的情況。

首先,我們需要能夠推斷出我們要綁定的函數的返回類型、類的類型、和參數類型。我們用一個函數模板來做到這一點。

template <typename R, typename T, typename Arg>
simple_bind_t<R,T,Arg> simple_bind(
  R (T::*fn)(Arg),
  const T& t,
  const placeholder&) {
  return simple_bind_t<R,T,Arg>(fn,t);
}

這看起來有點可怕,畢竟這只是在定義整個機器的一部分。但是,這一部分的焦點在於類型推斷在哪發生。你會注意到這個函數有三個模板參數,RT, 和 ArgR 是返回的類型,T是類的類型,而 Arg 是(單個)參數的類型。這些模板參數組成了我們的函數的第一個參數,即 R (T::*f)(Arg). 這樣,傳遞一個帶單個參數的成員函數給 simple_bind 將允許編譯器推斷出 R 爲成員函數的返回類型,T 爲成員函數的類,Arg 爲成員函數的參數類型。simple_bind 的返回類型是一個函數對象,它使用與 simple_bind 相同的三個類型進行特化,其構造函數接受一個成員函數指針和一個對應類(T)的實例。 simple_bind 簡單地忽略佔位符(即函數的最後一個參數),我保留這個參數的原因是爲了模仿 Boost.Bind 的語法。在一個更好的實現中,我們顯然應該使用這個參數,但是現在讓我們先不要管它。這個函數對象的實現相當簡單。

template <typename R,typename T, typename Arg>
class simple_bind_t {
  typedef R (T::*fn)(Arg);
  fn fn_;
  T t_;
public:
  simple_bind_t(fn f,const T& t):fn_(f),t_(t) {}

  R operator()(Arg& a) {
    return (t_.*fn_)(a);
  }
};

從 simple_bind 的實現中我們可以看到,構造函數接受兩個參數:第一個是指向成員函數的指針,第二個是一個 const T 引用,它會被複制並稍後用於給定一個用戶提供的參數來調用其成員函數。最後,調用操作符返回 R, 即成員函數的返回類型,並接受一個 Arg 參數,即傳給成員函數的那個參數的類型。調用成員函數的語法稍稍有點晦澀:

(t_.*fn_)(a);

.* 是成員指針操作符,它的第一個操作數是 class T; 另外還有一個成員指針操作符,->*, 它的第一個操作數是是一個 T 指針。剩下就是創建一個佔位符,即用於替換實際參數的變量。我們可以通過在匿名名字空間中包含某種類型的變量來創建一個佔位符;我們把它稱爲 placeholder:

namespace {
  class placeholder {};
  placeholder _1;
}

我們創建一個簡單的類和一個小程序來測試一下。

class Test {
public:
  void do_stuff(const std::vector<int>& v) {
    std::copy(v.begin(),v.end(),
      std::ostream_iterator<int>(std::cout," "));
  }
};

int main() {
  Test t;
  std::vector<int> vec;
  vec.push_back(42);
  simple_bind(&Test::do_stuff,t,_1)(vec);
}

當我們用上述參數實例化函數 simple_bind 時,類型被自動推斷;R 是 voidT 是 Test, 而Arg 是一個 const std::vector<int> 引用。函數返回一個simple_bind_t<void,Test,Arg> 的實例,我們立即調用它的調用操作符,並傳進一個參數vec.

非常不錯,simple_bind 已經給了你關於綁定器如何工作的一些想法。現在,是時候回到 Boost.Bind 了!

關於佔位符和參數

第一個例子示範了 bind 最多可以支持九個參數,但瞭解多一點關於參數和佔位符如何工作的情況,可以讓我們更好地使用它。首先,很重要的一點是,普通函數與成員函數之間有着非常大的差異,在綁定一個成員函數時,bind 表達式的第一個參數必須是成員函數所在類的實例!理解這個規則的最容易的方法是,這個顯式的參數將取替隱式的 this ,被傳遞給所有的非靜態成員函數。細心的讀者將會留意到,實際上這意味着對於成員函數的綁定器來說,只能支持八個參數,因爲第一個要用於傳遞實際的對象。以下例子定義了一個普通函數 print_string 和一個帶有成員函數 print_string 的類 some_class ,它們將被用於 bind 表達式。

#include <iostream>
#include <string>
#include "boost/bind.hpp"

class some_class {
public:
  typedef void result_type;
  void print_string(const std::string& s) const {
    std::cout << s << '\n';
  }
};

void print_string(const std::string s) {
  std::cout << s << '\n';
}

int main() {
  (boost::bind(&print_string,_1))("Hello func!");
  some_class sc;
  (boost::bind(&some_class::print_string,_1,_2))
    (sc,"Hello member!");
}

第一個 bind 表達式綁定到普通函數 print_string. 因爲該函數要求一個參數,因此我們需要用一個佔位符(_1)來告訴 bind 它的哪一個參數將被傳遞爲 print_string 的第一個參數。要調用獲得的函數對象,我們必須傳遞一個 string 參數給調用操作符。參數是一個const std::string&, 因此傳遞一個字面的字符串將引發一個 std::string 轉型構造函數的調用。

(boost::bind(&print_string,_1))("Hello func!");

第二個綁定器用於一個成員函數,some_class 的 print_string 。bind 的第一個參數是成員函數指針。但是,一個非靜態成員函數指針並不真的是一個指針[3]。我們必須要有一個對象纔可以調用這個函數。這就是爲什麼這個 bind 表達式必須聲明綁定器有兩個參數,調用它時兩個參數都必須提供。

[3] 是的,我知道這聽起來很怪異。但它的確是真的。

boost::bind(&some_class::print_string,_1,_2);

要看看爲什麼會這樣,就要考慮一下得到的這個函數對象要怎麼使用。我們必須把一個some_class 實例和一個 print_string 用的參數一起傳遞給它。

(boost::bind(&some_class::print_string,_1,_2))(sc,"Hello member!");

這個調用操作符的第一個參數是 this ,即那個 some_class 實例。注意,這第一個參數可以是一個指針(智能的或裸的)或者是一個引用;bind 是非常隨和的。調用操作符的第二個參數是那個成員函數要用的參數。這裏,我們"延遲"了所有兩個參數,即我們定義的這個綁定器,它的兩個參數,對象本身及成員函數的參數,都要在調用操作符時才指定。我們不是一定非這樣做不可。例如,我們可以創建一個綁定器,每次調用它時,都是對同一個對象調用 print_string ,就象這樣:

(boost::bind(&some_class::print_string,some_class(),_1))
 ("Hello member!");

這次得到的函數對象已經包含了一個 some_class 實例,因此它的調用操作符只需要一個佔位符(_1)和一個參數(一個string)。最後,我們還可以創建一個所謂的無參(nullary)函數,它連那個 string 也綁定了,就象這樣:

(boost::bind(&some_class::print_string,
 some_class(),"Hello member!"))();

這些例子清楚地顯示了 bind 的多功能性。它可用於延遲它所封裝的函數的所有參數、部分參數、或一個參數也不延遲。它也可以把參數按照你所要的順序進行重排;只要照你的需要排列佔位符就行了。接下來,我們將看看如何用 bind 來就地創建排序用的謂詞。

動態的排序標準

在對容器中的元素進行排序時,我們有時候需要創建一個函數對象以定義排序的標準,如果我們沒有提供關係操作符,或者是已有的關係操作符不是我們想要的排序標準時,就需要這樣做了。有些時候我們可以使用來自標準庫的比較函數對象(std::greater,std::greater_equal, 等等),但只能對已有類型進行比較,我們不能就地定義一個新的。我們將使用一個名爲 personal_info 的類來演示 Boost.Bind 如何幫助我們。personal_info 包含有 first name, last name, 和 age, 並且它沒有提供任何的比較操作符。這些信息在創建以後就不再變動,並且可以用成員函數 namesurname, 和 age 來取出。

class personal_info {
  std::string name_;
  std::string surname_;
  unsigned int age_;

public:
  personal_info(
    const std::string& n,
    const std::string& s,
    unsigned int age):name_(n),surname_(s),age_(age) {}

  std::string name() const {
    return name_;
  }

  std::string surname() const {
    return surname_;
  }

  unsigned int age() const {
    return age_;
  }
};

我們通過提供以下操作符來讓這個類可以流輸出(OutputStreamable):

std::ostream& operator<<(
  std::ostream& os,const personal_info& pi) {
  os << pi.name() << ' ' <<
    pi.surname() << ' ' << pi.age() << '\n';
  return os;
}

如果我們要對含有類型 personal_info 元素的容器進行排序,我們就需要爲它提供一個排序謂詞。爲什麼開始的時候我們沒有爲 personal_info 提供關係操作符呢?一個原因是,因爲有幾種排序的可能性,而我們不知道對於不同的用戶哪一種是合適的。雖然我們也可以選擇爲不同的排序標準提供不同的成員函數,但這樣會加重負擔,我們要在類中實現所有相關的排序標準,這並不總是可以做到的。幸運的是,我們可以很容易地用 bind 就地創建所需的謂詞。我們先看看基於年齡(可以通過成員函數 age 取得)來進行排序。我們可以爲此創建一個函數對象。

class personal_info_age_less_than :
  public std::binary_function<
  personal_info,personal_info,bool> {
public:
  bool operator()(
  const personal_info& p1,const personal_info& p2) {
    return p1.age()<p2.age();
  }
};

我們讓 personal_info_age_less_than 公有派生自 binary_function. 從 binary_function派生可以提供使用適配器時所需的 typedef ,例如使用 std::not2. 假設有一個 vector,vec, 含有類型爲 personal_info 的元素,我們可以象這樣來使用這個函數對象:

std::sort(vec.begin(),vec.end(),personal_info_age_less_than());

只要不同的比較方式的數量很有限,這種方式就可以工作良好。但是,有一個潛在的問題,計算邏輯被定義在不同的地方,這會使得代碼難以理解。利用一個較長的、描述清晰的名字可以解決這個問題,就象我們在這裏做的一樣,但是不是所有情況都會這樣清晰,有很大可能我們需要爲大於、小於或等於關係提供一堆的函數對象。

那麼,Boost.Bind 有什麼幫助呢?實際上,在這個例子中它可以幫助我們三次。如果我們要解決這個問題,我們發現有三件事情要做,第一件是綁定一個邏輯操作,如 std::less. 這很容易,我們可以得到第一部分代碼。

boost::bind<bool>(std::less<unsigned int>(),_1,_2);

注意,我們通過把 bool 參數提供給 bind,顯式地給出了返回類型。有時這是需要的,對於有缺陷的編譯器或者在無法推斷出返回類型的上下文時。如果一個函數對象包含typedefresult_type, 就不需要顯式給出返回類型[4]。現在,我們有了一個接受兩個參數的函數對象,兩個參數的類型都是 unsigned int, 但我們還不能用它,因爲容器中的元素的類型是 personal_info, 我們需要從這些元素中取出 age 並把它作爲參數傳遞給std::less. 我們可以再次使用 bind 來實現。

[4] 標準庫的函數對象都定義了 result_type ,因此它們可以與 bind 的返回類型推斷機制共同工作。

boost::bind(
  std::less<unsigned int>(),
  boost::bind(&personal_info::age,_1),
  boost::bind(&personal_info::age,_2));

這裏,我們創建了另外兩個綁定器。第一個用主綁定器的調用操作符的第一個參數(_1)來調用 personal_info::age 。第二個用主綁定器的調用操作符的第二個參數(_2)來調用personal_info::age 。因爲 std::sort 傳遞兩個 personal_info 對象給主綁定器的調用操作符,結果就是對來自被排序的 vector 的兩個 personal_info 分別調用personal_info::age 。最後,主綁定器傳遞兩個新的、內層的綁定器的調用操作符所返回的 age 給 std::less. 這正是我們所需要的!調用這個函數對象的結果就是 std::less 的結果,這意味着我們有了一個有效的比較函數對象可以用來排序容器中的 personal_info 對象。以下是使用它的方法:

std::vector<personal_info> vec;
vec.push_back(personal_info("Little","John",30));
vec.push_back(personal_info("Friar", "Tuck",50));
vec.push_back(personal_info("Robin", "Hood",40));

std::sort(
  vec.begin(),
  vec.end(),
  boost::bind(
    std::less<unsigned int>(),
    boost::bind(&personal_info::age,_1),
    boost::bind(&personal_info::age,_2)));

我們可以簡單地通過綁定另一個 personal_info 成員(變量或函數)來進行不同的排序,例如,按 last name 排序。

std::sort(
  vec.begin(),
  vec.end(),
  boost::bind(
    std::less<std::string>(),
    boost::bind(&personal_info::surname,_1),
    boost::bind(&personal_info::surname,_2)));

這是一種出色的技術,因爲它提供了一個重要的性質:就地實現簡單的函數。它使得代碼易懂且易於維護。雖然技術上可以用綁定器實現基於複雜標準的排序,但那樣做是不明智的。給 bind 表達式添加複雜的邏輯會很快失去它的清晰和簡潔。雖然有時你想用綁定來做更多的事情,但最好是讓綁定器與要維護它的人一樣聰明,而不是更加聰明。

函數組合,Part I

一個常見的問題是,將一些函數或函數對象組合成一個函數對象。假設你需要測試一個int ,看它是否大於5且小於等於10。使用"常規"的代碼,你將這樣寫:

if (i>5 && i<=10) {
  // Do something
}

如果是處理一個容器中的元素,上述代碼只有放在一個單獨的函數時才能工作。如果你不想這樣,那麼用一個嵌套的 bind 也可以獲得相同的效果(注意,這時通常不能使用標準庫的 bind1st 和 bind2nd)。如果我們對這個問題進行分解,我們會發現我們需要:邏輯與(std::logical_and), 大於(std::greater), 和小於等於(std::less_equal)。邏輯與看起來就象這樣:

boost::bind(std::logical_and<bool>(),_1,_2);

然後,我們需要另一個謂詞來回答 _1 是否大於5。

boost::bind(std::greater<int>(),_1,5);

然後,我們還需要另一個謂詞來回答 _1 是否小於等於10。

boost::bind(std::less_equal<int>(),_1,10);

最後,我們需要把它們兩個用邏輯與合起來,就象這樣:

boost::bind(
  std::logical_and<bool>(),
  boost::bind(std::greater<int>(),_1,5),
  boost::bind(std::less_equal<int>(),_1,10));

這樣一個嵌套的 bind 相對容易理解,雖然它是後序的。還有,任何人都可以逐字地閱讀這段代碼並弄清楚它的意圖。我們用一個例子來測試一下這個綁定器。

std::vector<int> ints;

ints.push_back(7);
ints.push_back(4);
ints.push_back(12);
ints.push_back(10);

int count=std::count_if(
  ints.begin(),
  ints.end(),
  boost::bind(
    std::logical_and<bool>(),
    boost::bind(std::greater<int>(),_1,5),
    boost::bind(std::less_equal<int>(),_1,10)));

std::cout << count << '\n';

std::vector<int>::iterator int_it=std::find_if(
  ints.begin(),
  ints.end(),
  boost::bind(std::logical_and<bool>(),
    boost::bind(std::greater<int>(),_1,5),
    boost::bind(std::less_equal<int>(),_1,10)));

if (int_it!=ints.end()) {
  std::cout << *int_it << '\n';
}

使用嵌套的 bind 時,小心地對代碼進行正確的縮入非常重要,因爲如果一旦縮入錯誤,代碼就會很難理解。想想前面那段清晰的代碼,再看看以下這個容易混亂的例子。

std::vector<int>::iterator int_it=
  std::find_if(ints.begin(),ints.end(),
    boost::bind<bool>(
    std::logical_and<bool>(),
    boost::bind<bool>(std::greater<int>(),_1,5),
      boost::bind<bool>(std::less_equal<int>(),_1,10)));

當然,對於較長的代碼行,這是一個常見的問題,但是在使用這裏所描述的結構時更爲明顯,在這裏長語句是合理的而不是個別例外。因此,請對你之後的程序員友好些,確保你的代碼行正確縮入,這樣可以讓人更容易閱讀。

本書的一位認真的審閱者曾經問過,在前面的例子中,爲什麼創建了兩個相同的綁定器,而不是創建一個綁定器對象然後使用兩次?答案是,因爲我們不知道 bind 所創建的綁定器的精確類型(它是由實現定義的),我們沒有方法爲它聲明一個變量。還有,這個類型通常都非常複雜,因爲它的署名特徵包括了函數 bind 中所有的類型信息(自動推斷的)。但是,可以用另外一個工具來保存得到的函數對象,例如來自 Boost.Function 的工具。相關方法的詳情請見 "Library 11Function 11"。

這裏給出的函數組合的要點與標準庫的一個著名的擴充相符,即來自SGI STL的函數compose2 ,它在 Boost.Compose 庫(現在已經不用了)中也被稱爲 compose_f_gx_hx 。

發佈了34 篇原創文章 · 獲贊 7 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章