16

ユーザーパラメーターのセットに基づいて実行中に設定される関数ポインターをセットアップしようとしています。関数ポインターが非静的メンバー関数を指すようにしたいのですが、その方法が見つかりません。

私が見た例では、これは静的メンバー関数のみでのみ実行できるか、ストレート C でグローバル変数を使用できると述べています。

簡単な例を次に示します。

    class CA
    {
    public:
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(double f(const double), double x) {
            char cTemp[256];
            sprintf_s(cTemp, "Value = %f", f(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
    };

実装部分は

CA cA;
cA.setA(1.0);
cA.setB(2.0);

double (*p)(const double);

if(true) {
    p = &cA.getA;  //'&' : illegal operation on bound member function expression
} else {
    p = cA.getB;  //'CA::getB': function call missing argument list; use '&CA::getB' to create a pointer to member
                  //'=' : cannot convert from 'double (__thiscall CA::* )(const double)' to 'double (__cdecl *)(const double)'
}

cA.print(p, 3.0);

では、p が「getA」または「getB」を指すようにして、「print」で引き続き使用できるようにするにはどうすればよいでしょうか。

私が見たところ、boost または std::bind を使用することをお勧めしますが、これらのいずれも使用したことがありません。これらに飛び込む必要がなく、何かが足りないことを願っています。

コンパイラ MSVC++ 2008

4

3 に答える 3

16

メンバー関数は暗黙のパラメーターを受け入れることを忘れないでthisください。したがって、 を受け入れるメンバー関数は、 を受け入れるdouble非メンバー (フリー) 関数と同じにすることはできませんdouble

// OK for global functions
double (*p)(const double);

// OK for member functions
double (CA:*p)(const double);

また、それらを呼び出す方法も異なります。まず第一に、メンバー関数では、それらを呼び出すオブジェクトが必要です (そのアドレスは最終的にthis関数呼び出しでポインターにバインドされます)。次に、.*演算子 (->*ポインターを介して呼び出しを実行している場合は演算子) を使用する必要があります。

p = &CA::getA;
CA cA;
(cA.*p)();

一貫して、 function の定義を変更する必要がありますprint():

    #include <iostream>

    void print(double (CA::*f)(const double), double x) 
    {
        // Rather use the C++ I/O Library if you can...
        std::cout << "Value = " << (this->*f)(x);
    };

main()最後に、関数を次のように書き換えます。

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    double (CA::*p)(const double);

    if (true) // Maybe use some more exciting condition :-)
    {
        p = &CA::getA;
    } 
    else {
        p = &CA::getB;
    }

    cA.print(p, 3.0);
}
于 2013-02-23T01:25:53.967 に答える
8

コンパイルの問題

この回答は、質問で提示されたコンパイルの問題に焦点を当てています。これを解決策として実装することはお勧めしません。

メンバ関数へのポインタは、typedefs とマクロで処理するのが最適です。

メンバー関数を呼び出すマクロは次のとおりです。

#define CALL_MEMBER_FN(object, ptrToMember)  ((object).*(ptrToMember))

ソース: [33.6] メンバー関数へのポインターを使用してメンバー関数を呼び出すときに構文エラーを回避するにはどうすればよいですか? C++ FAQ

(object).*(ptrToMember)これにより、ポインターでメンバー関数を呼び出すたびに、醜い構文を覚える必要がなくなります。

で、呼び出されclassた を宣言します。これにより、変数の宣言がはるかに簡単になります。typedefCAGetter

class CA
{
public:    
    typedef double (CA::*CAGetter)(const double x);

print()次に、関数を非常に簡単に宣言できます。

    void print(CAGetter f, double x)

本文も単純明快で簡潔です。

    {
        std::cout << "value = " << CALL_MEMBER_FN(*this, f)(x) << '\n';
    }

使用例:

CA a;
a.setA(3.1);
a.setB(4.2);

// Using a variable...
CA::CAGetter p = &CA::getA;
a.print(p, 1);

// without a variable
a.print(&CA::getB, 1);

// Calling the functions from outside the class...
std::cout << "From outside (A): " << CALL_MEMBER_FN(a, p)(10) << std::endl;
std::cout << "From outside (B): " << CALL_MEMBER_FN(a, &CA::getB)(10) << std::endl;

設計上の問題

メンバー関数へのポインターを同じクラスのインスタンスのメソッドに渡すのは、設計上の臭いです (通常、メンバー変数をメソッドに渡すことはありません。これも例外ではありません)。この質問には、根本的な設計の問題に対処するのに十分な情報はありませんが、この問題はおそらく、別のprint()メソッド、メンバー変数、または継承とポリモーフィズムで解決できます。

于 2013-02-23T01:51:58.963 に答える
0

メソッドへのポインターを使用できます。

class CA
{
    public:
        typedef double (CA::*getter)( double );
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(getter f, double x) {
            char cTemp[256];
            sprintf(cTemp, "Value = %f", (this->*f)(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
};

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    CA::getter p;

    if(true) {
            p = &CA::getA;  
    } else {
            p = &CA::getB; 
    cA.print( p, 3.0 );
}

または、boost::bind を使用します

class CA
{
    public:
        typedef boost::function<double( double )> getter;
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(getter f, double x) {
            char cTemp[256];
            sprintf(cTemp, "Value = %f", f(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
};

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    CA::getter p;

    if(true) {
            p = boost::bind( &CA::getA, &cA, _1 );
    } else {
            p = boost::bind( &CA::getB, &cA, _1 );
    }
    cA.print( p, 3.0 );
}
于 2013-02-23T01:35:48.067 に答える