0

ジェネリック プログラミングについては、十分にわかっていないと思うので、もっと学ぼうとしています。そこで、自分のプログラムの 1 つのテンプレート バージョンをどのように実装するかを考えています。私がこれを行おうとしているプログラムは、ユーザーが使用する積分器 (Euler、Runge Kutta など) を選択し、選択した関数を積分する数値積分プログラムです。これを行う私の現在の方法は、Integrator と呼ばれる抽象基本クラスと、統合メソッドを実装するいくつかの派生クラスを用意することです。したがって、コードは次のようになります (さらに多くのことが行われますが、これは方法論を示すためのものです)。これには Qt を使用し、Integrator *integrator; を宣言していることに注意してください。MainWindow クラスで。

void MainWindow::on_integrateButton_clicked() {
string whichIntegrator = getUserChoice();

integrator = getIntegrator( whichIntegrator, whichFunction, order );
integrator->setUp( data ); // things like initial conditions, time, step size, etc...

runIntegratorInNewThread();
}

基本的にファクトリメソッドを使用する getIntegrator を使用

// x being current data, xdot being the results of evaluating derivatives
typedef void (*pFunction)(const double t, const double x[], double *xdot);

Integrator* getIntegrator( const string &whichIntegrator, pFunction whichFunction, int order  ) {
    if (whichIntegrator == "Euler") {
        return new Euler(whichFunction, order);
    } else if (whichIntegrator == "RungeKutta") {
        return new RungeKutta(whichFunction, order);
    }
}

したがって、この方法はうまく機能し、インテグレーター プログラムは非常にうまく動作します。テンプレート関数はコンパイル時に生成されることがわかりました。実行時の情報を使用しているとしたら、テンプレートを使用してこれをどのように実装しますか? 質問が明確でない場合、私が求めているのは... 実行時にユーザーが選択した場合、どのインテグレーターを使用するか、テンプレート メソッドを使用して正しい統合関数を呼び出すにはどうすればよいですか?

4

2 に答える 2

1

テンプレートは特効薬ではありません。テンプレートを使用して多くのことができますが、現在使用しているポリモーフィズムの力を軽視しないでください。

これはテンプレートで行うことができますか? 答えは「はい」です。C++11 と shared_ptr を使用すると、次のようになります。

template<class T>
std::shared_ptr<T> getIntegrator(pFunction whichFunction, int order)
{
    return std::make_shared<T>(whichFunction, order);
}

そしてあなたの呼び出し元で:

std::shared_ptr<Integrator> integrator;
if (whichIntegrator  == "Euler")
{
    integrator = getIntegrator<Euler>(whichFunction, order);
}
else if(whichIntegrator  == "RungeKutta")
{
    integrator = getIntegrator<RungeKutta>(whichFunction, order);
}

もう1つの注意点は、ここでメモリリークに非常に注意する必要があるということです。あなたは新しくなり、反対しているのです。決して解放しないと、リークが発生します。

そうは言っても、この回答が、テンプレートを使用できますが、この場合はお勧めしませんが、ここではポリモーフィズムがうまく機能することを示していることを願っています。この例は、非常に単純で冗長なケースで、実際のテンプレートを示しているだけです

于 2013-03-28T17:44:25.920 に答える
1

このシステム全体を静的に型付けされたインテグレーターで書きたいとします。

私はあなたon_integrateButton_clickedのを取り、それを次のように変更します:

void MainWindow::on_integrateButton_clicked() {
  string whichIntegrator = getUserChoice();

  runInNewThread( [whichIntegrator,whichFunction,order]() {
    struct functor {
      FunctionType func;
      OrderType order;
      functor( FunctionType func_in, OrderType order_in):func(std::move(func_in)), order(std::move(order_in)) {}
      template<typename Integrator>
      void operator()( Integrator* integrator ) {
        // code using integrator here, not a virtual interface to it, an actual instance of the final type
      }
    };
    RunWithChosenIntegrator( whichIntegrator, functor(whichFunction,order) );
  } );
}

ご覧のとおり、コードは少し逆になっているようです。

型の選択を可能な限り延期し、その時点でfunctor、積分器へのポインターを使用して a を呼び出します。これは、インテグレーターを使用するコードがインテグレーターの完全な型情報を持ち、それを抽象的に扱っていないことを意味します。

ただし、通常、この種の問題にはランタイム ポリモーフィズムまたは型消去で十分です。

さて、RunWithChosenIntegratorちょっと変わった獣です。次のような署名があります。

template<typename Functor>
void RunWithChosenIntegrator( std::string const&whichIntegrator, Functor&& func ) {
  if (whichIntegrator == "bob") {
    BobIntegrator bob;
    func( &bob );
  } else if (whichIntegrator == "alice" ) {
    AliceIntegrator alice;
    func( &alice ):
  }
}

ご覧funcのとおり、パラメーターに基づいて異なるタイプのオブジェクトで呼び出しwhichIntegratorます。ifメタプログラミングを使用して/チェーンを生成する楽しい方法もありますがelse if、基本的なテンプレート プログラミングに慣れるまでは、おそらく学ぶ価値はありません。

Functor func、呼び出すすべての型へのポインターを受け入れることができる必要があります。簡単な例は、基本クラスへのポインターを受け取るだけで、上記で示したものはテンプレート型funcを取ります。T*

これらすべては、ランタイム ポリモーフィズムのオーバーヘッドが高すぎる場合、またはランタイム ポリモーフィズムを介してキャプチャするのが困難または不可能である不均一な方法でさまざまなインテグレータ クラスと対話する必要がある場合にのみ、実行する価値があります。

これがここに当てはまるとは思えません。

于 2013-03-28T18:13:38.537 に答える