1

関数へのポインタを引数として取る関数があります。

void MySolver::set_pt2func(double(*pt2function)(double*))
{
    pt2Function = pt2function;
}   

別のクラスでは、一部のメソッドが次のコードでインスタンスmy_solverを初期化します。

double (MyClass::*pt2apply)(double* parameter) = NULL; 
pt2apply = &MyClass::apply_func;

my_solver->set_pt2func(pt2apply);

最後の行はコンパイルエラーを示しています:

error: double (MyClass::*pt2apply)(double*)

argument of type  (MyClass::* )(double*) is not compatible 
with argument of type double(*)(double*)

直し方?

ありがとう!

編集

このヒントを使用します

http://www.parashift.com/c++-faq/memfnptr-to-const-memfn.html

typedefを設定しました:

typedef  double (MyClass::*Pt2apply_func)(double*) const;

私の方法では、これを試します:

Pt2apply_func pt2apply = &MyClass::apply_func;

my_solver->set_pt2func(pt2apply);

コンパイルエラーは&MyClass、互換性のあるタイプで表示されます。私の問題を処理する他の方法は?ありがとう!

4

4 に答える 4

6

関数へのポインターが必要です。

double(*)(double*)

あなたが持っているのは、メンバー関数へのポインタです:

double (MyClass::*)(double*)

これらは同じものではありません。または、すべてが似ています。最初のものは直接呼び出すことができます。->*2 つ目は、 or.*演算子を使用して、オブジェクトを介してのみ呼び出すことができます。

これは、コンパイラが型にうるさいというだけではありません。メンバー関数へのポインターは、カバーの下のポインター (または少なくとも単なるポインターではない) ではありません。動的バインディングを処理するには、より多くの情報が必要です。

詳細については、 http://www.parashift.com/c++-faq/pointers-to-members.htmlを参照してください。

C スタイルの API でメンバ関数へのポインタを使用する「古典的な」方法は、別のMyClass*場所から取得するラッパー関数を作成することです (通常は、関数ポインタとともに渡される「userinfo」、「thunk」など)。 ) と呼び出しますmyobj->*pmf(args)。次に、単純な関数であるため、そのラッパーへのポインターを渡すことができます。それを行うためのよりスマートな最新の方法にはbind、ラムダが含まれます。

最初に現代的な方法で対処しましょう。これははるかに単純であり、可能であれば行うべきことだからです。まず、C++11 を使用している場合はstd::bind、 、 またはを使用するかstd::mem_fn、単にラムダを記述できます。

auto pt2apply = [&](double *parameter) { return my_solver->pt2apply(parameter); }

さて、このものの型は ではありませんdouble(*)(double*)。実際、それは特定されていないものであり、おそらく言葉では言い表せないほど醜いものです. ここではauto、それを処理することを避けるために使用しましたが、 に渡す必要がset_pt2funcあり、そのメソッドはそれをメンバー変数に格納する必要があるため、入力できる何らかの種類の型が必要です。答えはstd::function<double(double *)>です。double *これは、 で呼び出すことができ、を返すあらゆるもののコピーを格納できるオブジェクトですdouble。そう:

void MySolver::set_pt2func(std::function<double(double*)> pt2function)
{
    pt2Function = pt2function;
}   

以上です。

現在、これには C++11 が必要です。C++98 を使用している場合はboost::lambda、C++11 ラムダの代わりに使用できます (かなり醜いですが、基本的な考え方は同じです)。または、( Boost ライブラリに基づいたboost::bindC++11 とほぼ同じ)、または同様の機能を持つ他の多くのライブラリのいずれかを使用できます。std::bindそしてファンクタを格納しますboost::function(これも C++11 とほぼ同じですがstd::function、効率が悪い場合があります)。(TR1 を備えた C++03 を持っている場合、stdまたはstd::tr1に C++11 の機能の一部が含まれている可能性がありますが、C++11 がほぼ完成するまで、さまざまなコンパイラ メーカーがすべての詳細を整理したわけではありません。それを使用する最も簡単な方法はboost::tr1.

なんらかの理由で C++98 に行き詰まり、Boost がない場合はstd::mem_fn、 を使用して、結果をstd::binary_function. これはかなり醜いので、ここでは詳細を説明しません。

それでは、古典的な解決策を見てみましょう。これは、コールバックなどに関数ポインタを使用する既存の API があり、それを変更できない場合にのみ行う価値があります。のような C 関数を使用するサンプル コードを探すことをお勧めしますqsort_r。これは、考え方がほぼ同じであるためです。関数ポインタを「サンク」と一緒に渡し、2 つの関数は常に一緒に移動します。(最新のソリューションはすべて、基本的に、サンクを追跡する必要なく呼び出すことができる単一のオブジェクトに 2 つのものをバインドすることであり、コードを読み書きしやすくし、コンパイラがエラーをキャッチしやすくします。 )

ここで重要なのは、関数ポインターを受け取ることができる単純な C スタイルの関数であるラッパー関数が必要であるということです。サンクは、MyClassオブジェクトへのポインターになります。私の例では、サンクを として渡しvoid *ます。これは明らかに劣っていますが (記述するコードが多くなり、コンパイラがエラーをキャッチする可能性が低くなります)、対処する必要がある汎用 C API。

したがって、すべてをまとめると、次のようになります。

void MySolver::set_pt2func(double(*pt2function)(double*, void *), void *userinfo)
{
    pt2Function = pt2function;
    userInfo = userinfo;
}

double wrapMyClassPt2Apply(double *parameter, void *userinfo)
{
    return ((MyClass*)userinfo)->apply_func(parameter);
}

my_solver->set_pt2func(wrapMyClassPt2Apply, (void *)my_solver);

メンバー関数へのポインターを使用していた場合は、ラッパーpt2applyでそれを呼び出すことに注意してください。->*しかし、もう必要ありpt2applyません。ラッパーは でapply_funcメンバーを呼び出すことができ->ます。

次に、MySolver が関数を呼び出したい場合、次のようにします。

pt2Function(parameter, userInfo);

最後に 1 つ: wrapMyClassPt2Apply をメンバー関数にすることはできません。それへのポインターは、ポインターではなくメンバー関数へのポインターになります。したがって、次の 2 つの選択肢があります。

  1. 無料の機能にしてください。これは完全に合理的なことです。フリー関数は、クラスのインターフェースの一部にすることができます。または、プライベート メソッドよりも隠しておくことができます (.cpp ファイルに配置し、.h ファイルではまったく言及しないことにより)。

  2. 静的メソッドにします。これを好む人もいますが、実際に有益なのは、 MyClass.cpp の外部の誰かがset_pt2func呼び出しを実行しようとしていて、すでに のフレンド (またはサブクラス) にMyClassなっている場合だけです。保護されています)。

于 2012-09-28T07:52:03.860 に答える
1

簡単な解決策はありません。メンバー関数へのポインターは、呼び出されるオブジェクトを必要とするため、異なります。をどのようMySolverに提供しMyClassますか? 基本的に、その答えが得られると、根本的な問題はほとんどなくなります。2 つの一般的な解決策は、MySolverが について知っていることMyClassです。この場合、set_pt2func の署名を修正するかMySolver、 の結果を提供できますstd::bind(pt2apply, SomeObj, _1)

于 2012-09-28T07:46:50.357 に答える
0

メンバー関数へのポインターは、通常のもの (C スタイルの関数ポインター) と互換性がありません。これは、メンバーがthis署名の一部ではない暗黙的な引数を取るためです。同様の動作を実現したい場合は、boost::bind をご覧ください。

于 2012-09-28T07:46:16.643 に答える
-1

Cの代わりにC++11を使用します。これは、メンバー関数をバインドするためstd::functionにとを使用することを意味します。std:mem_fn

于 2012-09-28T08:20:39.613 に答える