程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> std::bind技術內幕

std::bind技術內幕

編輯:關於C++

引子

最近群裡比較熱鬧,大家都在山寨c++11的std::bind,三位童孩分別實現了自己的bind,代碼分別在這裡:

木頭雲的實現:連接稍後補上。

mr.li的實現:https://code.google.com/p/y-code-svn/source/browse/#svn%2Ftrunk%2Fc%2B%2B%2FBex%2Fsrc%2FBex%2Fbind

null的實現:http://www.cnblogs.com/xusd-null/p/3693817.html#2934538

這些實現思路和ms stl的std::bind的實現思路是差不多的,只是在實現的細節上有些不同。個人覺得木頭雲的實現更簡潔,本文中的簡單實現也是基於木頭雲的bind之上的,在此表示感謝。下面我們來分析一下bind的基本原理。

bind的基本原理

bind的思想實際上是一種延遲計算的思想,將可調用對象保存起來,然後在需要的時候再調用。而且這種綁定是非常靈活的,不論是普通函數、函數對象、還是成員函數都可以綁定,而且其參數可以支持占位符,比如你可以這樣綁定一個二元函數auto f = bind(&func, _1, _2);,調用的時候通過f(1,2)實現調用。關於bind的用法更多的介紹可以參考我博客中介紹:http://www.cnblogs.com/qicosmos/p/3302144.html。

要實現一個bind需要解決兩個問題,第一個是保存可調用對象及其形參,第二個是如何實現調用。下面來分析如何解決這兩個問題。

保存可調用對象

實現bind的首先要解決的問題是如何將可調用對象保存起來,以便在後面調用。要保存可調用對象,需要保存兩個東西,一個是可調用對象的實例,另一個是可調用對象的形參。保存可調用對象的實例相很簡單,因為bind時直接要傳這個可調用對象的,將其作為一個成員變量即可。而保存可調用對象的形參就麻煩一點,因為這個形參是變參,不能直接將變參作為成員變量。如果要保存變參的話,我們需要用tuple來將變參保存起來。

可調用對象的執行

bind的形參因為是變參,可以是0個,也可能是多個,大部分情況下是占位符,還有可能占位符和實參都有。正是由於bind綁定的靈活性,導致我們不得不在調用的時候需要找出哪些是占位符,哪些是實參。如果某個一參數是實參我們就不處理,如果是占位符,我們就要將這個占位符替換為對應的實參。比如我們綁定了一個三元函數:auto f = bind(&func, _1, 2, _2);調用時f(1,3);由於綁定時有三個參數,一個實參,兩個占位符,調用時傳入了兩個實參,這時我們就要將占位符_1替換為實參1,占位符_2替換為實參3。這個占位符的替換需要按照調用實參的順序來替換,如果調用時的實參個數比占位符要多,則忽略多余的實參。

調用的實參,我們也會先將其轉換為tuple,用於在後面去替換占位符時,選取合適的實參。

bind實現的關鍵技術

將tuple展開為變參

前面講到綁定可調用對象時,將可調用對象的形參(可能含占位符)保存起來,保存到tuple中了。到了調用階段,我們就要反過來將tuple展開為可變參數,因為這個可變參數才是可調用對象的形參,否則就無法實現調用了。這裡我們會借助於一個整形序列來將tuple變為可變參數,在展開tuple的過程中我們還需要根據占位符來選擇合適實參,即占位符要替換為調用實參。

根據占位符來選擇合適的實參

這個地方比較關鍵,因為tuple中可能含有占位符,我們展開tuple時,如果發現某個元素類型為占位符,則從調用的實參生成的tuple中取出一個實參,用來作為變參的一個參數;當某個類型不為占位符時,則直接從綁定時生成的形參tuple中取出參數,用來作為變參的一個參數。最終tuple被展開為一個變參列表,這時,這個列表中沒有占位符了,全是實參,就可以實現調用了。這裡還有一個細節要注意,替換占位符的時候,如何從tuple中選擇合適的參數呢,因為替換的時候要根據順序來選擇。這裡是通過占位符的模板參數I來選擇,因為占位符place_holder<I>的實例_1實際上place_holder<1>, 占位符實例_2實際上是palce_holder<2>,我們是可以根據占位符的模板參數來獲取其順序的。

bind的簡單實現

#include <tuple>   
#include <type_traits>   
using namespace std;   
       
template<int...>   
struct IndexTuple{};   
       
template<int N, int... Indexes>   
struct MakeIndexes : MakeIndexes<N - 1, N - 1, Indexes...>{};   
       
template<int... indexes>   
struct MakeIndexes<0, indexes...>   
{   
    typedef IndexTuple<indexes...> type;   
};   
       
template <int I>   
struct Placeholder   
{   
};   
       
Placeholder<1> _1; Placeholder<2> _2; Placeholder<3> _3; Placeholder<4> _4; Placeholder<5>    
       
_5; Placeholder<6> _6; Placeholder<7> _7;   
Placeholder<8> _8; Placeholder<9> _9; Placeholder<10> _10;   
       
// result type traits   
       
template <typename F>   
struct result_traits : result_traits<decltype(&F::operator())> {};   
       
template <typename T>   
struct result_traits<T*> : result_traits<T> {};   
       
/* check function */
       
template <typename R, typename... P>   
struct result_traits<R(*)(P...)> { typedef R type; };   
       
/* check member function */
template <typename R, typename C, typename... P>    
struct result_traits<R(C::*)(P...)> { typedef R type; };   
       
template <typename T, class Tuple>   
inline auto select(T&& val, Tuple&)->T&&   
{   
    return std::forward<T>(val);   
}   
       
template <int I, class Tuple>   
inline auto select(Placeholder<I>&, Tuple& tp) -> decltype(std::get<I - 1>(tp))   
{   
    return std::get<I - 1>(tp);   
}   
       
// The invoker for call a callable
// 
		

測試代碼:

void TestFun1(int a, int b, int c)   
{   
}   
       
void TestBind1()   
{   
    Bind(&TestFun1,  _1,  _2,  _3)(1, 2, 3);   
    Bind(&TestFun1, 4, 5, _1)(6);   
    Bind(&TestFun1, _1, 4, 5)(3);   
    Bind(&TestFun1, 3, _1,  5)(4);   
}

bind更多的實現細節

由於只是展示bind實現的關鍵技術,很多的實現細節並沒有處理,比如參數是否是引用、右值、const volotile、綁定非靜態的成員變量都還沒處理,僅僅供學習之用,並非是重復發明輪子,只是展示bind是如何實現, 實際項目中還是使用c++11的std::bind為好。null同學還圖文並茂的介紹了bind的過程:http://www.cnblogs.com/xusd-null/p/3698969.html,有興趣的童孩可以看看.

關於bind的使用

在實際使用過程中,我更喜歡使用lambda表達式,因為lambda表達式使用起來更簡單直觀,lambda表達式在絕大多數情況下可以替代bind。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved