次のようにオブジェクトを使用して戦略パターンを実装する計算機クラスがあるとstd::function
します(Scott Meyers、Effective C ++:プログラムとデザインを改善する55の特定の方法を参照)。
class Calculator
{
public:
...
std::vector<double> Calculate(double x, double y)
{
std::vector<double> res;
for(const Function& f : functions)
res.push_back(f(x,y));
return res;
}
private:
std::vector<Function> functions;
};
どこ
typedef std::function<double(double,double)> Function;
これが私が直面している問題です。関数f
とg
、両方のタイプFunction
が、最終結果を得るために内部で高価で同一の計算を実行するとします。効率を向上させるために、すべての一般的なデータをでラップし、struct
それを1回計算して、引数として提供することができます。ただし、この設計にはいくつかの欠陥があります。たとえば、これにより、のシグネチャが変更され、Function
一部の関数実装に不要な引数が渡される可能性があります。さらに、これらの共通データと内部データは、コード内の他のコンポーネントから隠されなくなり、コードの単純さを損なう可能性があります。
次の最適化戦略について説明したいと思います。次のようなクラスCacheFG
を実装します。
- 与えられたdoubleと;
Update
のペアを使用して内部データを計算するメソッドを定義します。とx
y
- 現在の内部データが指定されたdoubleと
Check
のペアで計算されたかどうかを判別するメソッドを定義します。x
y
次にできることは、クラスの共通インスタンスを作成f
して共有することです。これは、構成を使用して実行できます。したがって、以下は、補助関数とを使用した関数との作成です。g
CacheFG
std::shared_ptr
f
g
f_aux
g_aux
double f_aux(double x, double y, const std::shared_ptr<CacheFG>& cache)
{
if(not cache->Check(x,y))
cache->Update(x,y);
...
}
std::shared_ptr<CacheFG> cache;
Function f = std::bind(f_aux, _1, _2, cache);
Function g = std::bind(g_aux, _1, _2, cache);
私の質問は次のとおりです。(1)これは最適化のための安全なアプローチですか?(2)この問題を解決するためのより良いアプローチはありますか?
編集:いくつかの回答の後、ここでの私の意図は、C++でメモ化手法を実装することであることがわかりました。私の目的には、最後に計算された状態だけで十分であることに注意してください。
DeadMGのおかげで、彼のアプローチを改善したものをここに書きます。彼のアイデアは、可変個引数テンプレートを使用したメモ化手法を使用することで構成されています。非参照型のみでstd::decay<Args>::type
の定義を確実にするために構成を使用する、わずかな変更を提供します。tuple
そうしないと、const-reference引数を持つ関数がコンパイルエラーを引き起こします。
template<typename Ret, typename... Args>
std::function<Ret(Args...)> MemoizeLast(std::function<Ret(Args...)> f)
{
std::tuple<typename std::decay<Args>::type...> cache;
Ret result = Ret();
return [=](Args... args) mutable -> Ret
{
if(std::tie(args...) == cache)
return Ret(result);
cache = std::make_tuple(args...);
return result = f(args...);
};
}
の移動を防ぐために、提供されたものがキャッシュされている場合result
は、そのコピーが返されます(return Ret(result)
) 。args