2013-07-23 117 views
4

有時我傾向於寫函數,而不是爲了維護函數調用之間的狀態,而是因爲我想捕獲函數調用之間共享的一些參數。舉個例子:Functors vs. std :: bind

class SuperComplexAlgorithm 
{ 
    public: 
     SuperComplexAlgorithm(unsigned int x, unsigned int y, unsigned int z) 
      : x_(x), y_(y), z_(z) 
     {} 

     unsigned int operator()(unsigned int arg) const /* yes, const! */ 
     { 
      return x_ * arg * arg + y_ * arg + z_; 
     } 

    private: 
     // Lots of parameters are stored as member variables. 
     unsigned int x_, y_, z_; 
}; 

// At the call site: 
SuperComplexAlgorithm a(3, 4, 5); 
for(unsigned int i = 0; i < 100; ++i) 
    do_stuff_with(a); // or whatever 

相比

unsigned int superComplexAlgorithm(unsigned int x, unsigned int y, 
            unsigned int z, unsigned int arg) 
{ 
    return x * arg * arg + y * arg + z; 
} 

// At the call site: 
auto a = std::bind(superComplexAlgorithm, 3, 4, 5, std::placeholders::_1); 
for(unsigned int i = 0; i < 100; ++i) 
    do_stuff_with(a); 

第一個解決方案有很多缺點的,在我看來:什麼xyz做的是分裂

  • 文檔在不同的地方(構造函數,類定義,operator())。
  • 大量的鍋爐代碼。
  • 較不靈活。如果我想捕獲不同的參數子集,該怎麼辦?

是的,我只是意識到多麼有用boost::bindstd::bind即可。現在對於我的問題,我開始在很多代碼中使用它:在任何情況下,我應該考慮使用手寫無狀態函子來綁定泛型函數中的參數嗎?

+9

首先你應該考慮lambdas – stijn

+0

@stijn:我不想在調用站點定義'superComplexAlgorithm',因爲它超級複雜,在很多地方使用,需要測試和文檔等。我可以在這種情況下,我不明白lambdas是如何起作用的,儘管我在其他情況下使用它們很多。 –

+4

@MarkusMayr​​,你可以用lambda替換'std :: bind'。代替'的std ::綁定(superComplexAlgorithm,3,4,5,性病::佔位符:: _ 1)',你寫'[](INT X){返回superComplexAlgorithm(3,4,5,X); }'。 – avakar

回答

8

拉姆達的解決辦法是idomatic C++ 11路:

auto a = [&](int x){ return superComplexAlgorithm(3, 4, 5, x); }; 
for(unsigned int i = 0; i < 100; ++i) 
    do_stuff_with(a); 

採用書面函子手的好處是,你可以:

  1. 移動參數到捕捉,或轉換它們(你可以用lambda在C++ 1y中做到這一點,但還沒有 - 這也可以用bind
  2. 它有一個非匿名類型(所以你可以談論這個類型而不使用auto) - 同樣是t rue bind,但它也有一個非匿名的有意義的類型,你可以在沒有decltype的情況下得到整個表達式! C++ 1y再次使bind/lambda更好。
  3. 您可以完美轉發剩餘的參數(您可以在C++ 1y中使用lambda來做到這一點,而bind可以做到這一點)
  4. 您可以掌握捕獲數據的存儲方式(在一個指針?一個智能指針?),而不會搞亂創建實例的代碼。

你也可以做一些與手寫函子是不可能做到與bind和lambda表達式,像包裹的多種功能的重載集合到一個對象(可以或不可以共享名)非常強大的東西,但那種角落案件不會經常出現。

+1

爲什麼你在lambda中有捕獲條款?除非實際需要捕獲某些內容,否則不應使用捕獲。 –

+0

爲什麼通過參考捕獲,如果根本沒有捕獲? –

+0

@sebastianredl這使得它類似於綁定:通常你綁定的值不編譯時間常數,不然你不會用'bind'或拉姆達麻煩:一個普通的老功能會怎麼做。如果我錯了,運行時成本爲零,所以我將其包含在內。 – Yakk

4

是否有任何情況下,我應該考慮使用手寫無狀態函子來綁定泛型函數中的參數?

從評論:

我不想在調用點定義superComplexAlgorithm,因爲它是超級複雜,在很多地方使用,需要測試和文檔等。

如果superComplexAlgorithm的實現需要一堆代碼,並最終將其分解爲需要大量參數的不同函數,那麼使用提供跨內部實現的共享狀態的類可能會更好功能秒。其他那個角落的情況下,std::bind的行爲將等同於您在問題中提供的仿函數。

的一些注意事項,因爲你提到boost::bindstd::bind作爲替代品。你可能想測試你的實現是幹什麼的。例如,對於提升1.35(古)的bind操作將使每個參數的4份,VS2010將使7個副本std::bind雖然std::bind在GCC 4.7只做1.如果參數小,不會補到高成本,但如果您正在進行操作,或者您的物件的複印費用太高,請僅測量。

關於拉姆達,如果性能是一個問題,也是衡量它的行爲方式。它應該製作一個副本。如果性能不是問題,那麼lambda表達式對於捕獲的內容(用戶需要閱讀lambda的實現來解決這個問題)甚至在查看代碼時可能並不那麼明顯。