std :: function और std :: bind: वे क्या हैं, और उनका उपयोग कब किया जाना चाहिए?


127

मुझे पता है कि stdएल्गोरिदम के साथ क्या और कब उपयोग करना है, लेकिन मैं यह नहीं समझ पाया कि स्ट्रॉस्ट्रुप ने सी 11 11 एफएक्यू में उनके बारे में क्या कहा है ।

क्या कोई समझा सकता है कि क्या हैं std::bindऔर क्या std::functionहैं, जब उनका उपयोग किया जाना चाहिए, और नए-नए उदाहरणों के लिए कुछ उदाहरण दें?

जवाबों:


200

std::bindके लिए है आंशिक समारोह आवेदन

यह है, मान लीजिए कि आपके पास एक फ़ंक्शन ऑब्जेक्ट है fजो 3 तर्क लेता है:

f(a,b,c);

आप एक नया फ़ंक्शन ऑब्जेक्ट चाहते हैं, जो केवल दो तर्क लेता है, जैसे कि:

g(a,b) := f(a, 4, b);

gफ़ंक्शन का "आंशिक अनुप्रयोग" है f: मध्य तर्क पहले से ही निर्दिष्ट किया गया है, और जाने के लिए दो शेष हैं।

आप std::bindपाने के लिए उपयोग कर सकते हैं g:

auto g = bind(f, _1, 4, _2);

यह वास्तव में यह करने के लिए एक मजेदार वर्ग लिखने की तुलना में अधिक संक्षिप्त है।

आपके द्वारा लिंक किए गए लेख में और उदाहरण हैं। आप आम तौर पर इसका उपयोग तब करते हैं जब आपको कुछ एल्गोरिथ्म में एक फ़ंक्टर को पारित करने की आवश्यकता होती है। आपके पास एक फ़ंक्शन या फ़ंक्टर है जो लगभग वह कार्य करता है जो आप चाहते हैं, लेकिन एल्गोरिथ्म उपयोगों की तुलना में अधिक कॉन्फ़िगर करने योग्य (यानी अधिक पैरामीटर हैं)। तो आप कुछ मापदंडों के लिए तर्क बाँधते हैं, और एल्गोरिथ्म को भरने के लिए बाकी छोड़ देते हैं:

// raise every value in vec to the power of 7
std::transform(vec.begin(), vec.end(), some_output, std::bind(std::pow, _1, 7));

यहां, powदो मापदंडों को लेता है और किसी भी शक्ति को बढ़ा सकता है , लेकिन हम सभी की परवाह 7 की शक्ति तक बढ़ा रहे हैं।

एक सामयिक उपयोग के रूप में जो आंशिक फ़ंक्शन अनुप्रयोग नहीं है, bindकिसी फ़ंक्शन के तर्कों को फिर से आदेश दे सकता है:

auto memcpy_with_the_parameters_in_the_right_flipping_order = bind(memcpy, _2, _1, _3);

मैं इसे केवल इसलिए उपयोग करने की अनुशंसा नहीं करता क्योंकि आप एपीआई पसंद नहीं करते हैं, लेकिन इसके संभावित संभावित उपयोग हैं क्योंकि:

not2(bind(less<T>, _2, _1));

एक कम-से-या-समान कार्य है (कुल आदेश, ब्ला ब्ला)। यह उदाहरण आम तौर पर आवश्यक नहीं है क्योंकि पहले से ही एक है std::less_equal(यह <=ऑपरेटर के बजाय का उपयोग करता है <, इसलिए यदि वे लगातार नहीं हैं तो आपको इसकी आवश्यकता हो सकती है, और आपको क्लास के लेखक को भी क्लूस्टिक के साथ यात्रा करने की आवश्यकता हो सकती है)। यदि आप प्रोग्रामिंग की एक कार्यात्मक शैली का उपयोग कर रहे हैं तो यह परिवर्तन की तरह है।


18
सदस्य कार्यों के लिए कॉलबैक के लिए भी उपयोगी:myThread=boost::thread(boost::bind(&MyClass::threadMain, this))
rlduffy

15
बाँध की अच्छी व्याख्या। लेकिन इससे क्या std::function?
RedX

10
आपका powउदाहरण संकलन नहीं है। चूंकि powएक अधिभार समारोह है, आपको मैन्युअल रूप से निर्दिष्ट करना होगा कि कौन सा अधिभार है। बाध्यकारी इसे छोड़ नहीं सकता है जिसके परिणामस्वरूप फ़नकार के कॉलर द्वारा कटौती की जा सकती है। जैसेstd::transform(vec.begin(), vec.end(), out.begin(), std::bind((double (*)(double, int))std::pow, _1, 7));
MM

2
बहुत अच्छी तरह से समझाया गया है, लेकिन कभी-कभी 2 तर्क के रूप में उपयोग के std::bindसाथ आता thisहै। क्या आप इस उपयोग के मामले को विस्तृत कर सकते हैं?
मेंडेस

2
इसके अलावा "_1" से आपका मतलब है std::placeholders::_1। मुझे यह पता लगाने में थोड़ा समय लगा कि यह संकलन क्यों नहीं था।
terryg

25

Std :: function और std :: bind के मुख्य उपयोग में से एक है, अधिक कार्य करने वाले फ़ंक्शन पॉइंटर्स। आप कॉलबैक तंत्र को लागू करने के लिए इसका उपयोग कर सकते हैं। लोकप्रिय परिदृश्य में से एक यह है कि आपके पास कुछ फ़ंक्शन हैं जिन्हें निष्पादित करने में लंबा समय लगने वाला है लेकिन आप इसे वापस आने के लिए इंतजार नहीं करना चाहते हैं तो आप उस फ़ंक्शन को अलग थ्रेड पर चला सकते हैं और इसे फ़ंक्शन पॉइंटर दे सकते हैं कि यह होगा कॉलबैक पूरा होने के बाद।

इसका उपयोग करने के लिए एक नमूना कोड यहां दिया गया है:

class MyClass {
private:
    //just shorthand to avoid long typing
    typedef std::function<void (float result)> TCallback;

    //this function takes long time
    void longRunningFunction(TCallback callback)
    {
        //do some long running task
        //...
        //callback to return result
        callback(result);
    }

    //this function gets called by longRunningFunction after its done
    void afterCompleteCallback(float result)
    {
        std::cout << result;
    }

public:
    int longRunningFunctionAsync()
    {
        //create callback - this equivalent of safe function pointer
        auto callback = std::bind(&MyClass::afterCompleteCallback, 
            this, std::placeholders::_1);

        //normally you want to start below function on seprate thread, 
        //but for illustration we will just do simple call
        longRunningFunction(callback);
    }
};

5
यह एक बेहतरीन जवाब है। मैंने इस जवाब को खोजने के लिए सभी को देखा है। धन्यवाद @ शीतलशाह
terryg

क्या आप इस बात के लिए स्पष्टीकरण जोड़ सकते हैं कि क्यों बंधन इसे सुरक्षित बनाने में मदद करता है?
स्टीवन लू

मेरा बुरा ... मैंने यह कहने का इरादा नहीं किया कि यह अधिक "सुरक्षित" है। सामान्य फ़ंक्शन पॉइंटर भी टाइप करने योग्य होते हैं, लेकिन std :: फ़ंक्शन अधिक सामान्य है लैंबडास, संदर्भ कैप्चर, सदस्य विधियों आदि के साथ काम करने के लिए
शीतल शाह

bind (& MyClass :: afterCompleteCallback, यह, std :: प्लेसहोल्डर :: _ 1), परिभाषा में 1 के लिए 2 args, इसके बाद शून्यकॉलबैक (फ्लोट परिणाम), यह समझा सकता है?
15

1
@nonock सदस्य फ़ंक्शन के फ़ंक्शन बिंदुओं के लिए, हमें पहले तर्क के रूप में "इस" सूचक को पास करना होगा।
सोंज सुब्रन

12

std :: bind को बढ़ावा देने वाले बंधन में शामिल करने के प्रस्ताव के बाद पुस्तकालय में मतदान किया गया था, मुख्य रूप से यह आंशिक कार्य विशेषज्ञता है जहां आप कुछ मापदंडों को ठीक कर सकते हैं और दूसरों को मक्खी पर बदल सकते हैं। अब यह C ++ में लैम्ब्डा करने का लाइब्रेरी तरीका है। जैसा कि स्टीव जेसोप ने कहा है

अब जब C ++ 11 लैम्ब्डा कार्यों का समर्थन करता है तो मुझे std :: bind का उपयोग करने के लिए कोई प्रलोभन महसूस नहीं होता है। मैं लाइब्रेरी फ़ीचर की तुलना में भाषा सुविधा के साथ करी (आंशिक विशेषज्ञता) का उपयोग करूंगा।

एसटीडी :: फ़ंक्शन ऑब्जेक्ट पॉलीमॉर्फिक फ़ंक्शन हैं। मूल विचार सभी कॉल करने योग्य वस्तुओं को परस्पर विनिमय करने में सक्षम होना है।

मैं आपको आगे के विवरण के लिए इन दो लिंक की ओर संकेत करूंगा:

C ++ 11 में लैम्ब्डा फ़ंक्शन: http://www.nullptr.me/2011/10/12/c11-lambda-having-fun-with-brackets/#.UJmXu8XA9Z8

C ++ में कॉल करने योग्य इकाई: http://www.nullptr.me/2011/05/31/callable-entity/#.UJmXuMXA9Z8


5
std::bindलैम्ब्डा के बिना कभी भी अस्तित्व में नहीं था - उन दोनों विशेषताओं को C ++ 11 में पेश किया गया था। हमारे पास था bind1stऔर bind2ndजो सी ++ 11 बाइंड के क्षीण संस्करण थे।
MM

5

मैंने इसे C ++ में एक प्लगइन थ्रेड पूल बनाने के लिए लंबे समय पहले इस्तेमाल किया था; चूंकि फ़ंक्शन तीन पैरामीटर ले रहा था आप इस तरह से लिख सकते हैं

मान लीजिए कि आपके तरीके पर हस्ताक्षर हैं:

int CTask::ThreeParameterTask(int par1, int par2, int par3)

तीन मापदंडों को बांधने के लिए एक फ़ंक्शन ऑब्जेक्ट बनाने के लिए आप इस तरह से कर सकते हैं

// a template class for converting a member function of the type int function(int,int,int)
//to be called as a function object
template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
class mem_fun3_t
{
public:
    explicit mem_fun3_t(_Ret (_Class::*_Pm)(_arg1,_arg2,_arg3))
        :m_Ptr(_Pm) //okay here we store the member function pointer for later use
    {}

    //this operator call comes from the bind method
    _Ret operator()(_Class *_P, _arg1 arg1, _arg2 arg2, _arg3 arg3) const
    {
        return ((_P->*m_Ptr)(arg1,arg2,arg3));
    }
private:
    _Ret (_Class::*m_Ptr)(_arg1,_arg2,_arg3);// method pointer signature
};

अब, मापदंडों को बांधने के लिए, हमें एक बाइंडर फ़ंक्शन लिखना होगा। तो, यहाँ यह जाता है:

template<typename _Func,typename _Ptr,typename _arg1,typename _arg2,typename _arg3>
class binder3
{
public:
    //This is the constructor that does the binding part
    binder3(_Func fn,_Ptr ptr,_arg1 i,_arg2 j,_arg3 k)
        :m_ptr(ptr),m_fn(fn),m1(i),m2(j),m3(k){}


        //and this is the function object 
        void operator()() const
        {
            m_fn(m_ptr,m1,m2,m3);//that calls the operator
        }
private:
    _Ptr m_ptr;
    _Func m_fn;
    _arg1 m1; _arg2 m2; _arg3 m3;
};

और, binder3 वर्ग का उपयोग करने के लिए एक सहायक कार्य - bind3:

//a helper function to call binder3
template <typename _Func, typename _P1,typename _arg1,typename _arg2,typename _arg3>
binder3<_Func, _P1, _arg1, _arg2, _arg3> bind3(_Func func, _P1 p1,_arg1 i,_arg2 j,_arg3 k)
{
    return binder3<_Func, _P1, _arg1, _arg2, _arg3> (func, p1,i,j,k);
}

और यहां हमें इसे कॉल करने का तरीका बताया गया है

F3 f3 = PluginThreadPool::bind3( PluginThreadPool::mem_fun3( 
          &CTask::ThreeParameterTask), task1,2122,23 );

नोट: f3 (); टास्क 1 को कॉल करेगा-> थ्रीपैरमीटरटैस्क (21,22,23);

अधिक जानकारी के लिए -> http://www.codeproject.com/Articles/26078/AC-Plug-in-Treadread-ool-Design

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.