我怀疑这是不可能的,但我想我会问。假设我有一个方法类:
class A {
public:
void b(int c);
};
我可以指向该成员函数:
void (A::*ptr)(int) = &A::b;
(someAInstance.*ptr)(123);
我也可以滥用函数指针并创建一个直接接收A
参数的指针(我不知道这是否安全,但它可以在我的机器上运行):
void (*ptr2)(A*, int) = (void (*)(A*, int))&A::b;
(*ptr2)(&someAInstance, 123);
我想要的是以某种方式讨论A
参数,并创建一个只需int
的函数指针,但调用A::b
方法我预定义的特定A
实例。 A
实例将为该特定函数指针保持不变,但可能有几个函数指针都指向相同的A::b
方法,但使用不同的A
实例。例如,我可以创建一个单独的包装函数:
A* someConstantA = new A;
void wrapper(int c) {
someConstantA->b(c);
}
void (*ptr3)(int) = &wrapper;
现在我可以使用ptr3
而不知道调用它的特定A
,但我必须定义一个特殊的函数来处理它。我需要一种方法来为任意数量的A
实例制作指针,因此我不能像那样硬编码。这有可能吗?
编辑:应该提到,我被困在C ++ 03的土地上,也无法使用Boost
答案 0 :(得分:3)
不要创建包装器功能,创建包装器 functor 。这允许您在可调用对象中封装您想要的任何状态(例如A*
)。
class A {
public:
void b(int c) {}
};
struct wrapper {
A* pA;
void (A::*pF)(int);
void operator()(int c) { (pA->*pF)(c); }
wrapper(A* pA, void(A::*pF)(int)) : pA(pA), pF(pF) {}
};
int main () {
A a1;
A a2;
wrapper w1(&a1, &A::b);
wrapper w2(&a2, &A::b);
w1(3);
w2(7);
}
答案 1 :(得分:1)
如果您使用的是C ++ 11,则可以使用lambda(未经测试的代码):
template<typename T, typename A>
std::function<void(A)> curry(T& object, void (T::*ptr)(A))
{
return [](A a) { (object.*ptr)(std::forward<A>(a)); }
}
答案 2 :(得分:1)
如果你有一个足够新的编译器(例如gcc 4.2+),它应该包含TR1,你可以使用std::tr1::bind
:
#include <cstdio>
#include <tr1/functional>
class A {
public:
void b(int c) {
printf("%p, %d\n", (void*)this, c);
}
};
int main() {
A* a = new A;
std::tr1::function<void(int)> f =
std::tr1::bind(&A::b, a, std::tr1::placeholders::_1); // <--
f(4);
delete a;
return 0;
}
在没有TR1的纯C ++ 03中也可行,但也更麻烦:
std::binder1st<std::mem_fun1_t<void, A, int> > f =
std::bind1st(std::mem_fun(&A::b), a);
您也可以编写自己的函数对象。
请注意,在上述所有情况下,您需要非常小心a
的生命周期,因为这是一个裸指针。使用std::tr1::bind
,您至少可以将指针包装在std::tr1::shared_ptr
中,这样它就可以与函数对象一样长。
std::tr1::shared_ptr<A> a (new A);
std::tr1::function<void(int)> f =
std::tr1::bind(&A::b, a, std::tr1::placeholders::_1);
答案 3 :(得分:0)
我正在使用Boost::bind。
基本上:
class A
{
int myMethod(int x)
{
return x*x;
}
};
int main(int argc, char* argv[])
{
A test();
auto callable = boost::bind(&A::myMethod, &A, _1);
// These two lines are equivalent:
cout << "object with 5 is: " << test.myMethod(5) << endl;
cout << "callable with 5 is: " << callable(5) << endl;
return 0;
}
我认为这应该有效。我也在这里使用auto来推断boost :: bind()在编译时返回的类型,编译器可能支持也可能不支持。有关bind返回类型的说明,请参阅stackoverflow中的other question。
Boost支持回到Visual Studio 2003(我认为),这一切都可以在那里工作,不过你认为我会使用BOOST_AUTO。请参阅已经链接的其他问题以获得解释。
答案 4 :(得分:0)
你想做什么是不可能的。
为了理解原因,假设它是可能的 - 函数指针必须指向可执行文件或其中一个库中的某个函数,因此它必须指向一个函数,该函数知道调用哪个A
实例,就像你的包装函数。因为A
的实例直到运行时才知道,所以你必须在运行时创建这些函数,这是不可能的。
你可以在C ++ 03中尝试做什么,只要你乐意传递函数对象而不是函数指针。
正如其他人已经给出了C ++ 11 lambdas,TR1和boost(所有这些都比下面更漂亮)的解决方案,但你提到你不能使用C ++ 11,我会贡献一个纯粹的C ++ 03:
int main()
{
void (A::*ptr)(int) = &A::b;
A someAInstance;
std::binder1st<std::mem_fun1_t<void,A,int> > fnObj =
std::bind1st(std::mem_fun(ptr), &someAInstance);
fnObj(321);
};
答案 5 :(得分:0)
我已经使用模板Delegate类进行了一些工作。
// T is class, R is type of return value, P is type of function parameter
template <class T, class R, class P> class Delegate
{
typedef R (T::*DelegateFn)(P);
private:
DelegateFn func;
public:
Delegate(DelegateFn func)
{
this->func = func;
}
R Invoke(T * object, P v)
{
return ((object)->*(func))(v);
}
};
class A {
private:
int factor;
public:
A(int f) { factor = f; }
int B(int v) { return v * factor; }
};
int _tmain(int argc, _TCHAR* argv[])
{
A * a1 = new A(2);
A * a2 = new A(3);
Delegate<A, int, int> mydelegate(&A::B);
// Invoke a1->B
printf("Result: %d\n", mydelegate.Invoke(a1, 555));
// Invoke a2->B
printf("Result: %d\n", mydelegate.Invoke(a2, 555));
_getch();
delete a1;
delete a2;
return 0;
}