5

我怀疑这是不可能的,但我想我会问。假设我有一个带有方法的类:

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

想要的是以某种方式对参数进行curry A,并创建一个函数指针,它只接受一个,但在我预定义的特定实例上int调用该方法。对于那个特定的函数指针,实例将保持不变,但可能有几个函数指针都指向同一个方法,但使用不同的实例。例如,我可以制作一个单独的包装函数:A::bAAA::bA

A* someConstantA = new A;
void wrapper(int c) {
    someConstantA->b(c);
}

void (*ptr3)(int) = &wrapper;

现在我可以ptr3在不知道A将调用发送到哪个特定对象的情况下使用它,但我必须定义一个特殊的函数来处理它。我需要一种方法来为任意数量的A实例创建指针,所以我不能像那样对其进行硬编码。这有可能吗?


编辑:应该提到,我被困在 C++03 领域,也不能使用 Boost

4

6 回答 6

3

不要创建包装函数,而是创建一个包装函子。这允许您将您想要的任何状态(例如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);
}
于 2012-04-02T20:08:52.790 回答
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)); }
}
于 2012-04-02T19:52:14.203 回答
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,您至少可以将指针包装在 a 中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);
于 2012-04-02T20:02:23.380 回答
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() 在编译时返回的类型,您的编译器可能支持也可能不支持。有关绑定的返回类型的说明,请参阅stackoverflow 上的另一个问题

Boost 支持回到 Visual Studio 2003(我认为),这一切都将在那里工作,尽管我认为您将使用 BOOST_AUTO。请参阅已链接的另一个问题以获取解释。

于 2012-04-02T20:10:33.087 回答
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);
};
于 2012-04-02T20:13:57.923 回答
0

我已经使用模板委托类解决了一些问题。

// 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;
}
于 2012-04-02T20:36:34.760 回答