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

私が望むのは、どうにかして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

十分に新しいコンパイラ (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);
于 2012-04-02T20:02:23.380 に答える
1

C++11 を使用している場合は、ラムダ (テストされていないコード) を使用できます。

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 に答える
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 までサポートしていると思います (私はそう思います)。説明については、すでにリンクされている他の質問を参照してください。

于 2012-04-02T20:10:33.087 に答える
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;
}
于 2012-04-02T20:36:34.760 に答える
0

あなたがしたいことは不可能です。その理由を理解するために、それが可能であると仮定してください。関数ポインターは、実行可能ファイルまたはそのライブラリーのいずれかのどこかにある関数を指している必要があるためA、ラッパー関数と同様に、どのインスタンスを呼び出すかを知っている関数を指している必要があります。のインスタンスはA実行時までわからないため、実行時にこれらの関数を作成する必要がありますが、これは不可能です。

関数ポインターではなく関数オブジェクトを渡すことができれば、C++03 でも可能です。

他の人が C++11 ラムダ、TR1、およびブースト (これらはすべて以下よりも優れています) を使用したソリューションを既に提供していますが、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 に答える