std :: function ve std :: bind: bunlar nedir ve ne zaman kullanılmalıdır?


129

Fonksiyonların ne olduğunu ve bunları stdalgoritmalarla ne zaman kullanacağımı biliyorum , ancak Stroustrup'un onlar hakkında C ++ 11 SSS'de ne dediğini anlamadım .

Kimse ne olduğunu, ne zaman kullanılması gerektiğini açıklayabilir std::bindve std::functionyeni başlayanlar için bazı örnekler verebilir mi?

Yanıtlar:


201

std::bindiçin kısmi işlevi uygulama .

Yani, f3 bağımsız değişken alan bir işlev nesneniz olduğunu varsayalım :

f(a,b,c);

Yalnızca iki argüman alan, şu şekilde tanımlanan yeni bir işlev nesnesi istiyorsunuz:

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

gfonksiyonun "kısmi uygulamasıdır" f: ortadaki bağımsız değişken zaten belirtilmiş ve gidecek iki tane kaldı.

Şunları elde std::bindetmek için kullanabilirsiniz g:

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

Bu, bunu yapmak için bir functor sınıfı yazmaktan daha kısadır.

Bağlandığınız makalede başka örnekler de var. Bunu genellikle bazı algoritmalara bir functor geçirmeniz gerektiğinde kullanırsınız. Neredeyse istediğiniz işi yapan, ancak algoritmanın kullandığından daha yapılandırılabilir (yani daha fazla parametresi olan) bir işleviniz veya functorunuz var . Dolayısıyla, bazı parametrelere bağımsız değişkenler bağlarsınız ve geri kalanını algoritmanın doldurması için bırakırsınız:

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

Burada powiki parametre alır ve herhangi bir güce yükseltilebilir , ancak tek umursadığımız 7'nin kuvvetine yükseltmektir.

Kısmi işlev uygulaması olmayan ara sıra kullanım olarak bind, argümanları bir işleve yeniden sıralayabilir:

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

API'yi sevmediğiniz için kullanmanızı önermiyorum, ancak potansiyel pratik kullanımları vardır, örneğin:

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

eşitten küçük veya eşit bir işlevdir (toplam bir sıra varsayarak, falan filan). Bu örnek normalde gerekli değildir, çünkü zaten bir vardır std::less_equal(bunun <=yerine işleci kullanır <, bu nedenle tutarlı değillerse, buna ihtiyacınız olabilir ve ayrıca bir ipucu ile sınıfın yazarını ziyaret etmeniz gerekebilir). Yine de, işlevsel bir programlama stili kullanıyorsanız ortaya çıkan türden bir dönüşümdür.


18
Üye işlevlerine yapılan geri aramalar için de kullanışlıdır:myThread=boost::thread(boost::bind(&MyClass::threadMain, this))
rlduffy

15
Bağlantının güzel açıklaması. Peki ya ne olacak std::function?
RedX

10
Sizin powörnek derleme yapmaz. Yana powbir aşırı işlev olduğu, el hangi aşırı belirtmek gerekir. Bağlama, sonuçta ortaya çıkan işlevin arayan tarafından çıkarılacak şekilde bırakılamaz. Örneğinstd::transform(vec.begin(), vec.end(), out.begin(), std::bind((double (*)(double, int))std::pow, _1, 7));
MM

2
Çok iyi açıklanmıştır, ancak bazen 2. argüman olarak kullanımla std::bindbirlikte gelir this. Lütfen bu kullanım örneğini açıklar mısınız?
Mendes

2
Ayrıca "_1" ile demek istiyorsun std::placeholders::_1. Bunun neden derlenmediğini anlamam biraz zaman aldı.
terryg

26

Std :: function ve std :: bind'in ana kullanımlarından biri, daha genelleştirilmiş işlev işaretçileridir. Geri arama mekanizmasını uygulamak için kullanabilirsiniz. Popüler senaryolardan biri, çalıştırılması uzun zaman alacak bazı işlevlere sahip olmanızdır, ancak geri dönmesini beklemek istemiyorsanız, bu işlevi ayrı bir iş parçacığında çalıştırabilir ve ona bir işlev işaretçisi verebilirsiniz. tamamlandıktan sonra geri arama.

İşte bunun nasıl kullanılacağına ilişkin örnek bir kod:

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
Bu harika bir cevap. Bu cevabı bulmak için her yere baktım. Teşekkürler @ShitalShah
terryg

Bağlamanın neden daha güvenli olmasına yardımcı olduğuna dair bir açıklama ekleyebilir misiniz?
Steven Lu

Benim hatam ... Daha "daha güvenli" olduğunu söylemek niyetinde değildim. Normal işlev işaretçileri de tür güvenlidir, ancak std :: function lambdalar, bağlam yakalama, üye yöntemleri vb.
İle

bind (& MyClass :: afterCompleteCallback, this, std :: placeholders :: _ 1), tanımda 1 için 2 argüman, void afterCompleteCallback (kayan sonuç), bunu açıklayabilir mi?
kilitlenmeyen

1
@nonock Üye işlevlerin işlev işaretçileri için, ilk argüman olarak "this" işaretçisini iletmemiz gerekir.
sanoj subran

12

std :: bind, tekliften sonra boost bind'i dahil etmek için kitaplığa oy verildi, öncelikle bu, birkaç parametreyi sabitleyebileceğiniz ve diğerlerini anında değiştirebileceğiniz kısmi işlev uzmanlığıdır. Şimdi bu, C ++ 'da lambdas yapmanın kütüphane yolu. Steve Jessop tarafından cevaplandığı gibi

C ++ 11 artık lambda işlevlerini desteklediğine göre artık std :: bind kullanmak için herhangi bir cazibe hissetmiyorum. Kitaplık özelliği yerine dil özelliği ile köri (kısmi uzmanlaşma) kullanmayı tercih ederim.

std :: function nesneleri polimorfik fonksiyonlardır. Temel fikir, tüm çağrılabilir nesnelere birbirinin yerine geçerek başvurabilmektir.

Daha fazla ayrıntı için sizi bu iki bağlantıya yönlendireceğim:

C ++ 11'de Lambda işlevleri: http://www.nullptr.me/2011/10/12/c11-lambda-having-fun-with-brackets/#.UJmXu8XA9Z8

C ++ 'da çağrılabilir varlık: http://www.nullptr.me/2011/05/31/callable-entity/#.UJmXuMXA9Z8


5
std::bindlambdas olmadan asla var olmadı - bu özelliklerin her ikisi de C ++ 11'de tanıtıldı. Biz var mıydı bind1stve bind2ndhangi C ++ 11 bağlama versiyonlarını deri bir kemik bulundu.
MM

5

C ++ 'da bir eklenti iş parçacığı havuzu oluşturmak için uzun süre kullandım; İşlev üç parametre aldığından bu şekilde yazabilirsiniz

Yönteminizin imzaya sahip olduğunu varsayalım:

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

Üç parametreyi bağlamak için bir işlev nesnesi oluşturmak için bunu yapabilirsiniz

// 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
};

Şimdi, parametreleri bağlamak için bir bağlayıcı fonksiyon yazmamız gerekiyor. İşte burada:

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;
};

Ve binder3 sınıfını kullanmak için bir yardımcı işlev - 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);
}

ve burada bize nasıl hitap edeceğiz

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

Not: f3 (); task1-> ThreeParameterTask (21,22,23) yöntemini çağıracaktır;

Daha kanlı ayrıntılar için -> http://www.codeproject.com/Articles/26078/AC-Plug-in-ThreadPool-Design

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.