Как работает параметр шаблона функции std::? (реализация)
на Бьярн Страуструпсайт (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 вызов фу функция-член?
параметр шаблона int(X*, int)
, is &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);
он говорит, что со специализацией шаблона мы можем анализировать параметры шаблона тип функции (офигенно)! Ниже приведен грязный / простой пример о том, как std::function может работать:
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;
}
Edit: благодаря Томек за его хороший намек о 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);
g++, похоже, имеет объединение, которое может содержать указатель функции, указатель члена или указатель void, который, вероятно, указывает на функтор. Добавьте перегрузки, которые соответствующим образом помечают, какой член Союза действителен и тяжелый кастинг в суп, а затем он работает...
Они не являются указателями функций. Для этого существует функция std::. Он обертывает любые вызываемые типы, которые вы ему даете. Вы должны проверить boost:: bind - он часто используется, чтобы сделать указатели функций-членов вызываемыми как (это, args).