Wiem, czym są funktory i kiedy używać ich z std
algorytmami, ale nie rozumiem, co mówi o nich Stroustrup w C ++ 11 FAQ .
Czy ktoś może wyjaśnić, co std::bind
i std::function
kiedy należy ich używać, i podać przykłady dla początkujących?
Wiem, czym są funktory i kiedy używać ich z std
algorytmami, ale nie rozumiem, co mówi o nich Stroustrup w C ++ 11 FAQ .
Czy ktoś może wyjaśnić, co std::bind
i std::function
kiedy należy ich używać, i podać przykłady dla początkujących?
Odpowiedzi:
std::bind
służy do częściowego zastosowania funkcji .
To znaczy, załóżmy, że masz obiekt funkcji, f
który przyjmuje 3 argumenty:
f(a,b,c);
Potrzebujesz nowego obiektu funkcji, który przyjmuje tylko dwa argumenty, zdefiniowane jako:
g(a,b) := f(a, 4, b);
g
jest "częściowym zastosowaniem" funkcji f
: środkowy argument został już określony i zostały jeszcze dwa.
Możesz użyć, std::bind
aby uzyskać g
:
auto g = bind(f, _1, 4, _2);
Jest to bardziej zwięzłe niż pisanie klasy funktora, aby to zrobić.
Dalsze przykłady znajdują się w artykule, do którego prowadzi łącze. Zwykle używa się go, gdy trzeba przekazać funktor do jakiegoś algorytmu. Masz funkcję lub funktor, który wykonuje prawie to, co chcesz, ale jest bardziej konfigurowalny (tj. Ma więcej parametrów) niż wykorzystuje algorytm. Więc wiążesz argumenty z niektórymi parametrami, a resztę pozostawiasz algorytmowi do wypełnienia:
// raise every value in vec to the power of 7
std::transform(vec.begin(), vec.end(), some_output, std::bind(std::pow, _1, 7));
Tutaj pow
przyjmuje dwa parametry i może podbić do dowolnej potęgi, ale wszystko, na czym nam zależy to podbicie do potęgi 7.
Jako okazjonalne użycie, które nie jest częściową aplikacją funkcji, bind
może również zmienić kolejność argumentów na funkcję:
auto memcpy_with_the_parameters_in_the_right_flipping_order = bind(memcpy, _2, _1, _3);
Nie polecam go używać tylko dlatego, że nie podoba ci się API, ale ma on potencjalne praktyczne zastosowania, na przykład, ponieważ:
not2(bind(less<T>, _2, _1));
jest funkcją mniejszą lub równą (zakładając całkowity porządek, bla bla). Ten przykład zwykle nie jest konieczny, ponieważ już istnieje std::less_equal
(używa <=
raczej operatora niż <
, więc jeśli nie są one spójne, możesz tego potrzebować, a także możesz potrzebować odwiedzić autora klasy za pomocą wskazówki). Jest to jednak rodzaj transformacji, która pojawia się, jeśli używasz funkcjonalnego stylu programowania.
std::function
?
pow
przykład się nie kompiluje. Ponieważ pow
jest to funkcja przeciążona, musisz ręcznie określić, które przeciążenie. Wiązanie nie może pozwolić, aby zostało to wywnioskowane przez wywołującego wynikowy funktor. Np.std::transform(vec.begin(), vec.end(), out.begin(), std::bind((double (*)(double, int))std::pow, _1, 7));
std::bind
pojawia się wraz z this
użyciem jako drugim argumentem. Czy możesz opisać ten przypadek użycia?
std::placeholders::_1
. Zajęło mi trochę czasu, aby dowiedzieć się, dlaczego to się nie kompiluje.
Jednym z głównych zastosowań std :: function i std :: bind są bardziej uogólnione wskaźniki funkcji. Możesz go użyć do zaimplementowania mechanizmu wywołania zwrotnego. Jednym z popularnych scenariuszy jest to, że masz jakąś funkcję, której wykonanie zajmie dużo czasu, ale nie chcesz czekać na jej powrót, możesz uruchomić tę funkcję w oddzielnym wątku i nadać jej wskaźnik funkcji, który będzie wywołanie zwrotne po jego zakończeniu.
Oto przykładowy kod, jak tego używać:
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);
}
};
std :: bind został wybrany do biblioteki po propozycji włączenia boost bind, przede wszystkim jest to częściowa specjalizacja funkcji, w której możesz naprawić kilka parametrów i zmienić inne w locie. Teraz jest to biblioteczny sposób robienia lambd w C ++. Jak odpowiedział Steve Jessop
Teraz, gdy C ++ 11 obsługuje funkcje lambda, nie czuję już pokusy, by używać std :: bind. Wolałbym używać curry (częściowa specjalizacja) z funkcją językową niż biblioteką.
Obiekty std :: function są funkcjami polimorficznymi. Podstawową ideą jest możliwość wymiennego odwoływania się do wszystkich wywoływalnych obiektów.
Aby uzyskać więcej informacji, chciałbym wskazać te dwa linki:
Funkcje lambda w C ++ 11: http://www.nullptr.me/2011/10/12/c11-lambda-having-fun-with-brackets/#.UJmXu8XA9Z8
Jednostka wywoływalna w C ++: http://www.nullptr.me/2011/05/31/callable-entity/#.UJmXuMXA9Z8
std::bind
nigdy nie istniało bez lambd - obie te funkcje zostały wprowadzone w C ++ 11. Mieliśmy, bind1st
a bind2nd
które były wychudzonymi wersjami wiązania C ++ 11.
Użyłem go dawno temu do stworzenia puli wątków wtyczek w C ++; Ponieważ funkcja przyjmowała trzy parametry, możesz pisać w ten sposób
Załóżmy, że Twoja metoda ma podpis:
int CTask::ThreeParameterTask(int par1, int par2, int par3)
Aby utworzyć obiekt funkcji do powiązania trzech parametrów, możesz zrobić w ten sposób
// 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
};
Teraz, aby powiązać parametry, musimy napisać funkcję binder. A więc oto jest:
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;
};
I funkcja pomocnicza do używania klasy 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);
}
a tutaj nas, jak to nazwać
F3 f3 = PluginThreadPool::bind3( PluginThreadPool::mem_fun3(
&CTask::ThreeParameterTask), task1,2122,23 );
Uwaga: f3 (); wywoła metodę task1-> ThreeParameterTask (21,22,23);
Aby uzyskać więcej szczegółów -> http://www.codeproject.com/Articles/26078/AC-Plug-in-ThreadPool-Design
myThread=boost::thread(boost::bind(&MyClass::threadMain, this))