Как работает параметр шаблона std:: function? (реализация)
на Бьярн Страуструпсайт (C++11 FAQ):
struct X { int foo(int); };
std::function<int(X*, int)> f;
f = &X::foo; //pointer to member
X x;
int v = f(&x, 5); //call X::foo() for x with 5
как это работает? Как это std:: function вызов foo функция-член?
параметр шаблона int(X*, int)
, составляет &X::foo
преобразован в указатель на функцию-член до указатель на функцию, не являющуюся членом?!
(int(*)(X*, int))&X::foo //casting (int(X::*)(int) to (int(*)(X*, int))
чтобы уточнить: я знаю, что нам не нужно использовать какой-либо указатель std:: function, но я не знаю, как внутренности std:: function обрабатывать эту несовместимость между a указатель на функцию-член и указатель на функцию, не являющуюся членом. Я не знаю, как стандарт позволяет нам реализовать что-то вроде std:: function!
4 ответа:
Спасибо за все ответы.
Я нашел хороший пример из раздела 14.8.2.5-21 стандарта:
template<class> struct X { }; template<class R, class ... ArgTypes> struct X<R(int, ArgTypes ...)> { }; template<class ... Types> struct Y { }; template<class T, class ... Types> struct Y<T, Types& ...> { }; template<class ... Types> int f(void (*)(Types ...)); void g(int, float); // uses primary template X<int> x1; // uses partial specialization; ArgTypes contains float, double X<int(int, float, double)> x2; // uses primary template X<int(float, int)> x3; // use primary template; Types is empty Y<> y1; // uses partial specialization; T is int&, Types contains float, double Y<int&, float&, double&> y2; // uses primary template; Types contains int, float, double Y<int, float, double> y3; // OK; Types contains int, float int fv = f(g);
он говорит, что с шаблоном специализации, мы можем проанализировать параметры шаблона a тип функции (офигенно)! Ниже приведен грязный / простой пример о том, как std:: функция может работать:
template<class T> struct Function { }; template<class T, class Obj, class... Args> struct Function<T(Obj*, Args...)> // Parsing the function type { enum FuncType { FuncTypeFunc, FuncTypeMemFunc }; union FuncPtr { T(*func)(Obj*, Args...); T(Obj::*mem_func)(Args...); }; FuncType m_flag; FuncPtr m_func_ptr; Function(T(*func)(Obj*, Args...)) // void(*)(Funny*, int, int) { m_flag = FuncTypeFunc; m_func_ptr.func = func; } Function(T(Obj::*mem_func)(Args...)) // void(Funny::*)(int, int) { m_flag = FuncTypeMemFunc; m_func_ptr.mem_func = mem_func; } void play(Obj* obj, Args... args) { switch(m_flag) { case FuncTypeFunc: (*m_func_ptr.func)(obj, args...); break; case FuncTypeMemFunc: (obj->*m_func_ptr.mem_func)(args...); break; } } };
использование:
#include <iostream> struct Funny { void done(int i, int j) { std::cout << "Member Function: " << i << ", " << j << std::endl; } }; void done(Funny* funny, int i, int j) { std::cout << "Function: " << i << ", " << j << std::endl; } int main(int argc, char** argv) { Funny funny; Function<void(Funny*, int, int)> f = &Funny::done; // void(Funny::*)(int, int) Function<void(Funny*, int, int)> g = &done; // void(*)(Funny*, int, int) f.play(&funny, 5, 10); // void(Funny::*)(int, int) g.play(&funny, 5, 10); // void(*)(Funny*, int, int) return 0; }
редактировать: спасибо Томек за его хороший намек о
union
s, приведенный выше пример изменено для хранения указателя функции-члена/не-члена в одной (не двух) переменной.
Edit:Мартин-Йорк правильно,переключатель не было хорошей идеей в приведенном выше примере, поэтому я полностью изменил пример, чтобы работать лучше:
template<class T> class Function { }; template<class Res, class Obj, class... ArgTypes> class Function<Res (Obj*, ArgTypes...)> // Parsing the function type { union Pointers // An union to hold different kind of pointers { Res (*func)(Obj*, ArgTypes...); // void (*)(Funny*, int) Res (Obj::*mem_func)(ArgTypes...); // void (Funny::*)(int) }; typedef Res Callback(Pointers&, Obj&, ArgTypes...); Pointers ptrs; Callback* callback; static Res call_func(Pointers& ptrs, Obj& obj, ArgTypes... args) { return (*ptrs.func)(&obj, args...); // void (*)(Funny*, int) } static Res call_mem_func(Pointers& ptrs, Obj& obj, ArgTypes... args) { return (obj.*(ptrs.mem_func))(args...); // void (Funny::*)(int) } public: Function() : callback(0) { } Function(Res (*func)(Obj*, ArgTypes...)) // void (*)(Funny*, int) { ptrs.func = func; callback = &call_func; } Function(Res (Obj::*mem_func)(ArgTypes...)) // void (Funny::*)(int) { ptrs.mem_func = mem_func; callback = &call_mem_func; } Function(const Function& function) { ptrs = function.ptrs; callback = function.callback; } Function& operator=(const Function& function) { ptrs = function.ptrs; callback = function.callback; return *this; } Res operator()(Obj& obj, ArgTypes... args) { if(callback == 0) throw 0; // throw an exception return (*callback)(ptrs, obj, args...); } };
использование:
#include <iostream> struct Funny { void print(int i) { std::cout << "void (Funny::*)(int): " << i << std::endl; } }; void print(Funny* funny, int i) { std::cout << "void (*)(Funny*, int): " << i << std::endl; } int main(int argc, char** argv) { Funny funny; Function<void(Funny*, int)> wmw; wmw = &Funny::print; // void (Funny::*)(int) wmw(funny, 10); // void (Funny::*)(int) wmw = &print; // void (*)(Funny*, int) wmw(funny, 8); // void (*)(Funny*, int) return 0; }
как это делается (я считаю) остается неопределенным (но у меня нет копии стандарта здесь).
но учитывая все различные возможности, которые должны быть охвачены у меня есть ощущение, что расшифровка точного определения того, как это работает будет очень трудно: так что я не буду пытаться.
но я думаю, что вы хотели бы знать, как функторы работы и они относительно просты. Итак, вот краткий пример.
функторы:
это объекты, которые действуют как функции.
Они очень полезны в коде шаблона, поскольку часто позволяют использовать объекты или функции взаимозаменяемо. Однако самое замечательное в функторах заключается в том, что они могут удерживать состояние (своего рода закрытие бедного человека).struct X { int operator()(int x) { return doStuff(x+1);} int doStuff(int x) { return x+1;} }; X x; // You can now use x like a function int a = x(5);
вы можете использовать тот факт, что functor hold state содержит такие вещи, как параметры или объекты или указатель на методы-члены (или любую их комбинацию).
struct Y // Hold a member function pointer { int (X::*member)(int x); int operator(X* obj, int param) { return (obj->*member)(param);} }; X x; Y y; y.member = &X::doStuff; int a = y(&x,5);
или даже пойти дальше и привязать параметры. Так теперь все, что вам нужно предоставить, это один из параметров.
struct Z { int (X::*member)(int x); int param; Z(int (X::*m)(int), int p) : member(m), param(p) {} int operator()(X* obj) { return (obj->*member)(param);} int operator()(X& obj) { return (obj.*member)(param);} }; Z z(&X::doStuff,5); X x; int a = z(x);