Curiously recurring template patternとC++の静的多態
12350 ワード
引用する
最近C++の数値ライブラリを探して、最后についに1つのほぼ完璧なeigenを探し当てて、インタフェースは优雅で、使いやすくて、スピードは速くて、ドキュメントは详しくて...ほとんど欠点はありませんが、欠点はライブラリの設計者が苦労しているのかもしれません...また,疎行列の部分は開発段階では完全に成熟していない.
マトリックス乗算などはboost::ublas(このライブラリの利点はboostの下で、他のeigenよりはるかに悪い..個人的には)prod(A,B)と書くのではなく、D=A*B*Cで直接書くことができます.
なぜublasは演算記号を再ロードしないのですか?効率の問題が原因でしょう.もちろんA*Bの書き方は優雅でmatlab文法に似ていますが、eigenではlazy evaluationを採用して効率の問題を避けています.
2つのベクトルの加算を考慮する
egigenでは、次のようなコードにコンパイルできます.
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
単純演算子でリロードすると、実際には次のコードに似ています.
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
つまり次のように
ダイナミックマルチステートアプリケーション、よく使われる設計モードtemplate method pattern
私たちが分詞器を書くことを考慮して、私たちはいくつかの異なる分詞器を書くことを考慮することができますが、主な分割プロセスsegmentは同じで、具体的な内部実装部分はいくつかの部品が異なる分詞器に対して異なる実装がある可能性があります.では、私はベースクラスSegBase実装という分割プロセスを抽出します.
部品のデフォルト実装を提供し、異なる分詞器はSegBaseを継承して同じ分割プロセスを継承するが、部品の実装を書き換えることができる.
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
結果は次のとおりです.
In SegIn Seg
ここでの問題はdealに対して虚関数を採用する必要があることであり、dealは非常に核心的なコードがフレームワークの流れの中で大量に呼び出される可能性があり、虚関数による効率損失が気になる可能性がある..もし私たちがsegment()を虚関数に変更し、dealを普通の関数に変更すれば、効率損失を避けて効果が得られないが、
次のように
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
結果は
In segBaseIn segBase
CRTP法を用いて静的マルチステートを達成する
CRTPモードの基本コードアーキテクチャは
CRTP法を用いて,上述した分詞器コード設計を完了し,効率損失の問題を回避するために虚関数の使用を回避することを考慮した.
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
実行結果:
Use Seg modified Use Seg modified Use SegBase defaultUse SegBase default
ライブラリの設計実装の観点から言えば、あまり優雅ではなく、虚関数のスキームほど優雅ではありません.サブクラスの再機能が必要であることを考慮して、すべてのパスコードを手動で書くのはstaticのようなものです.cast (this)->deal(); ,ライブラリの作者は虚関数を使うより面倒だと書いていますが、効果は悪くありません.使用者にとってインタフェースは同じ実現で透明で、速度の損失もなく、解決策です~:)、多くのC++テンプレートライブラリは実際にはeigen、openmeshなど、このような案を大量に採用しています.
静的多様性に注意するには、dealが単独でクラスに提案したように、テンプレートパラメータとして静的多様性を実現する他の可能性のある実装方法がありますが、設計階層はより複雑になります.もちろんcrtpには他の用途があります.参考
http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
最近C++の数値ライブラリを探して、最后についに1つのほぼ完璧なeigenを探し当てて、インタフェースは优雅で、使いやすくて、スピードは速くて、ドキュメントは详しくて...ほとんど欠点はありませんが、欠点はライブラリの設計者が苦労しているのかもしれません...また,疎行列の部分は開発段階では完全に成熟していない.
マトリックス乗算などはboost::ublas(このライブラリの利点はboostの下で、他のeigenよりはるかに悪い..個人的には)prod(A,B)と書くのではなく、D=A*B*Cで直接書くことができます.
なぜublasは演算記号を再ロードしないのですか?効率の問題が原因でしょう.もちろんA*Bの書き方は優雅でmatlab文法に似ていますが、eigenではlazy evaluationを採用して効率の問題を避けています.
2つのベクトルの加算を考慮する
u = v + w; // (*)
egigenでは、次のようなコードにコンパイルできます.
for(int i = 0; i < size; i++) u[i] = v[i] + w[i];
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
単純演算子でリロードすると、実際には次のコードに似ています.
VectorXf tmp = v + w;
VectorXf u = tmp;
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
つまり次のように
for(int i = 0; i < size; i++) tmp[i] = v[i] + w[i];
for(int i = 0; i < size; i++) u[i] = tmp[i];
tmp
size 2*size
C++0X , , std::move, u , tmp , ( gcc4.5)
lazy evaluation , eigen http://eigen.tuxfamily.org/dox-devel/TopicInsideEigenExample.html 。
ダイナミックマルチステートアプリケーション、よく使われる設計モードtemplate method pattern
私たちが分詞器を書くことを考慮して、私たちはいくつかの異なる分詞器を書くことを考慮することができますが、主な分割プロセスsegmentは同じで、具体的な内部実装部分はいくつかの部品が異なる分詞器に対して異なる実装がある可能性があります.では、私はベースクラスSegBase実装という分割プロセスを抽出します.
部品のデフォルト実装を提供し、異なる分詞器はSegBaseを継承して同じ分割プロセスを継承するが、部品の実装を書き換えることができる.
#include <iostream>
using namespace std;
class SegBase
{
public:
//
void segment()
{
for (int i = 0; i < 2; i++)
{
deal(); //
}
}
virtual void deal()
{
cout << "In segBase" << endl;
}
};
class Seg : public SegBase
{
public:
virtual void deal()
{
cout << "In Seg" << endl;
}
};
void run()
{
Seg seg;
seg.segment();
}
int main(int argc, char *argv[])
{
run();
return 0;
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
結果は次のとおりです.
In SegIn Seg
ここでの問題はdealに対して虚関数を採用する必要があることであり、dealは非常に核心的なコードがフレームワークの流れの中で大量に呼び出される可能性があり、虚関数による効率損失が気になる可能性がある..もし私たちがsegment()を虚関数に変更し、dealを普通の関数に変更すれば、効率損失を避けて効果が得られないが、
次のように
#include <iostream>
using namespace std;
class SegBase
{
public:
//
virtual void segment()
{
for (int i = 0; i < 2; i++)
{
deal(); //
}
}
void deal()
{
cout << "In segBase" << endl;
}
};
class Seg : public SegBase
{
public:
void deal()
{
cout << "In Seg" << endl;
}
};
void run()
{
Seg seg;
seg.segment();
}
int main(int argc, char *argv[])
{
run();
return 0;
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
結果は
In segBaseIn segBase
CRTP法を用いて静的マルチステートを達成する
CRTPモードの基本コードアーキテクチャは
// The Curiously Recurring Template Pattern (CRTP)
template <typename T>
struct base
{
// ...
};
struct derived : base<derived>
{
// ...
};
CRTP法を用いて,上述した分詞器コード設計を完了し,効率損失の問題を回避するために虚関数の使用を回避することを考慮した.
#include <iostream>
using namespace std;
template<typename Derived>
class SegBase
{
public:
//
void segment()
{
for (int i = 0; i < 2; i++)
{
deal(); //
}
}
void deal_default()
{
cout << "Use SegBase default" << endl;
}
void deal()
{
static_cast<Derived*> (this)->deal();
}
};
class Seg : public SegBase<Seg>
{
public:
void deal()
{ //Seg deal
cout << "Use Seg modified " << endl;
}
};
class Seg2 : public SegBase<Seg2>
{
public:
typedef SegBase<Seg2> Base;
void deal()
{ //Seg2 SegBase deal
Base::deal_default();
}
};
void run()
{
Seg seg;
seg.segment();
Seg2 seg2;
seg2.segment();
}
int main(int argc, char *argv[])
{
run();
return 0;
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
実行結果:
Use Seg modified Use Seg modified Use SegBase defaultUse SegBase default
ライブラリの設計実装の観点から言えば、あまり優雅ではなく、虚関数のスキームほど優雅ではありません.サブクラスの再機能が必要であることを考慮して、すべてのパスコードを手動で書くのはstaticのようなものです.cast
静的多様性に注意するには、dealが単独でクラスに提案したように、テンプレートパラメータとして静的多様性を実現する他の可能性のある実装方法がありますが、設計階層はより複雑になります.もちろんcrtpには他の用途があります.参考
http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern