17

私見おっと、デザインパターンは理にかなっており、実際に適用することができました。

しかし、Modern C++ のような「ジェネリック プログラミング / メタ プログラミング」 となると、私は混乱してしまいます。

-- 新しいプログラミング/設計パラダイムですか?

――「ライブラリ開発」に限ったことですか?そうでない場合、どのような設計/コーディング状況でメタ プログラミング/ジェネリック プログラミングを使用する必要があるか。

-- テンプレートを使用すると、ジェネリック プログラミングを行っているということですか?

私はこのトピックについて多くのことをグーグルで調べましたが、BIG PICTURE を完全には把握していません。この投稿も参照してください。


以下の議論を読んだ後、今までのところ、私は確信しています(まだ正しくないかもしれません):

a) ジェネリック プログラミングとメタ プログラミングは、2 つの異なる概念です。

4

9 に答える 9

30

メタプログラミングはかなりエキゾチックなトピックです。学ぶのは面白いです、それは強力なツールです、そして時々あなたはそれが役に立つと思うかもしれません。ただし、ツールボックスで最も頻繁に使用されるツールになることはありません。場合によっては、異なるプロパティを持つ一連の無関係な型にコードを作用させたいことがあります。そこでメタプログラミングが役立ちます。少し注意が必要ですが、引数の型が整数である場合にのみ使用できる関数のオーバーロードを記述できます。 、またはそれがポインタである場合、またはタイプX、Y、またはZのいずれかである場合(おそらくZの定数を無視します)。

それは本質的に型を使ったプログラミングです。通常、プログラムは2つの数値を取得して、3番目の数値を生成したり、数値が何らかの要件を満たしているかどうかを通知したりすることができます。メタプログラミングは、2つのタイプを取り、3番目のタイプを生成するか、タイプが何らかの要件を満たしているかどうかを通知します。そして、はい、それはおそらくライブラリ開発で主に役立ちます。しかし、繰り返しになりますが、ほとんどのコードはライブラリコードと見なすことができます。main()関数以外はすべてライブラリコードであると言えます。

通常、メタプログラミングを通じて問題を解決したい場合は、関連するBoostライブラリを使用して手間のかかる作業を行うことをお勧めします。Boost.TypeTraitsともちろんBoost.Mplは、あなたのために物事を本当に単純化することができます。しかし、それはあなたが知る必要のあるものではなく、あなたが頻繁に必要とする可能性のあるものでもありません。

ジェネリックプログラミングは関連しています(場合によっては、内部でメタプログラミングを使用して実際にジェネリックになることがあります。たとえば、標準ライブラリはメタプログラミングのタッチを使用して、生のポインターを有効なイテレーターに変換します。これは、「イテレーター」の概念がジェネリックであるために必要です) 、しかし完全に同じではありません。そしてそれははるかに広く使われています。

をインスタンス化するたびにstd::vector、ジェネリックプログラミングを使用します。一連の値を処理するためにイテレータのペアを使用するたびに、ジェネリックプログラミングを使用します。ジェネリックプログラミングは、コードが可能な限りジェネリックであり、どのタイプが記述されていても機能する必要があるという考えにすぎません。std :: vectorは、「ICanBeContained」インターフェースを実装するために包含型を必要としません(Javaがコンテナークラスに格納するためにObjectからすべてを派生させる必要があることを覚えていますか?つまり、プリミティブ型はボックス化され、型の安全性が失われること。これは一般的ではなく、無意味な制限です。)

イテレータを使用してシーケンスを反復処理するコードは一般的であり、任意のタイプのイテレータ、またはプレーンポインタでも機能します。

ジェネリックプログラミングは非常に広く有用であり、多くの場合、OOPを大幅に置き換えることができます。(上記の例を参照してください。その制限を回避できるのに、インターフェイスを実装するために含まれている型を必要とするコンテナーを作成するのはなぜですか?)

多くの場合、OOPでインターフェースを使用する場合、実行時にタイプを変更することはできませんが(もちろん、それは時々発生します)、コンパイル時に別のタイプにスワップできるようにします(おそらく、本格的な実装を使用するのではなく、テスト中にオブジェクトをモックする)、または単に2つのクラスを分離するため。ジェネリックプログラミングは、インターフェースを定義して維持するという面倒な作業をしなくても、それを行うことができます。そのような場合、ジェネリックプログラミングは、より少ないコードを記述して維持する必要があることを意味し、より優れたパフォーマンスとより優れた型安全性を実現します。そうです、あなたは間違いなくジェネリックプログラミングに慣れているはずです。C++はあまり良いOOP言語ではありません。OOPに厳密に固執したい場合は、Javaまたは他のよりOOPに固定された言語に切り替える必要があります。C++ではOOコードを書くことはできますが、それが最善の解決策ではないことがよくあります。標準ライブラリのほぼ全体がOOPではなくジェネリックプログラミングに依存しているのには理由があります。標準ライブラリには、継承やポリモーフィズムはほとんどありません。彼らはそれを必要としませんでした、そしてコードはそれなしでより使いやすくそしてより強力になりました。

そして、他の質問に答えるために、はい、ジェネリックプログラミングはほとんど別のパラダイムです。テンプレートメタプログラミングはそうではありません。これは型システムを操作するためのかなり特殊な手法であり、少数の問題を解決するのに非常に優れています。パラダイムと見なされるには、はるかに一般的に有用であり、機能、OO、ジェネリックプログラミングなど、基本的にすべてに使用できるアプローチである必要があると思います。

xtoflは本当にそれを釘付けにしたと思います:ジェネリックプログラミングはあなたのコードをタイプに気づかないようにすることです。(std :: vectorは気にしないか、どのタイプが格納されているかを知る必要があります。それは機能します。)

一方、メタプログラミングは型の計算に関するものです。タイプT0とT1が与えられた場合、整数N0とN1が与えられた場合と同じように、タイプT2を定義できます。N0とN1の合計であるN2を定義できます。

Boost.Mplライブラリには、この明らかな例があります。通常のコードでは、整数N0、N1、およびN2がある場合、これら3つの値を含むstd::vectorを作成できます。次に、他のアルゴリズムを使用してインデックスを計算し、ベクトル内のその場所に格納されている値を抽出できます。

タイプT0、T1、およびT2が与えられると、これら3つのタイプを含むmpl::vectorを作成できます。これで、他のアルゴリズムを使用してコンパイル時にインデックスを計算し、ベクター内のその場所に格納されている型を抽出できます。

于 2009-06-11T10:49:32.927 に答える
12

ジェネリックプログラミング(型を認識しない)とメタプログラミングを区別する必要があります。メタプログラミングは、型システム内で計算を行うための完全に合法的な方法です。

ジェネリックプログラミングは、多くのコードで一般化可能なパターンを見つけるときに非常に役立ちます。パターンの違いが変数値だけでなく、さまざまなタイプにもある場合は、ジェネリックプログラミングがコードのリファクタリングに役立ちます。

メタプログラミングは、まったく異なるドメインに適用できます。そのドメインは確かに非常に新しく、独自の調査が必要です。それ楽しいです!

非常に便利で一般的なメタプログラミングパターンの1つは、特性/ポリシーの概念です。

于 2009-06-11T10:52:33.293 に答える
10

C++ テンプレート メタプログラミングは、さまざまなアプリケーションに適用できる強力なコード難読化手法です。

  • チームの誰も理解できないコードを書きたいとき
  • 書いて7日経っても理解できないコードが欲しい時
  • コードのパフォーマンスが保守性よりも重要な場合
  • 履歴書のスキルとして「テンプレート メタプログラミング」を記載できるようにしたい場合。
  • 多くの実際のコンパイラで動作する可能性が低いコードを記述する必要がある場合
  • プリプロセッサ マクロを使用するよりもカミソリの刃を食べたい場合

もう 1 つのケース:

  • Boost ライブラリが内部でどのように機能するかを知りたい場合、またはそれらに貢献したい場合。

「ジェネリック プログラミング」(STL コンテナー、auto_ptrs などを考えてください) の違いは、C++ テンプレートが達成するように設計されたものであり、「テンプレート メタプログラミング」(テンプレート システムを使用してコンパイラに効果的に「アルゴリズムを実行」させること) です。重要。

私はすべて前者に賛成ですが、後者からの実際の利点を理解するのは難しいと感じています.

于 2009-06-11T09:58:13.710 に答える
5

高度なテンプレートとテクニックについては、AndreiAlexandrescuによるModernC ++Designをお勧めします。

C ++は厳格な言語であり、実行時のイントロスペクション機能は実質的にありません。あなたが遭遇するであろう多くの問題はテンプレートで対処することができます。また、テンプレート言語はチューリング完全であるため、複雑なデータ構造を生成したり、コンパイル時に値を事前に計算したりすることができます。多くのシナリオでは、それだけの価値があるよりも厄介な場合があります。

于 2009-06-11T09:46:16.527 に答える
4

まだ与えられていない1つの答え:ジェネリックプログラミングとメタプログラミングは確かに互いにまったく異なる技術ですが、どちらも(本質的に)同じ問題を解決するように設計されています:ボイラープレートを取り除く方法。これらは、ソフトウェアエンジニアリングの原則の1つだけでなく、2つの頭字語にも具体化されています。DRY(繰り返さないでください)とDIE(複製は悪です)です。これは、現代のオッカムの剃刀の言い換えです。つまり、「エンティティは必要以上に増やされてはなりません」(entia non sunt multiplicanda praeter necessitatem)。[14世紀の論理学者が21世紀のソフトウェア設計に役立つ何かを思い付くことができることを誰が知っていましたか?]。

とにかく、両方の手法のポイントは、「ほぼ同じ」コードを単一のパラメーター化されたコードに統合する方法を見つけることです。メタプログラミングの場合、コード生成を使用しているため、この手法では、一般的なコード(テンプレート、またはより一般的にはコード生成関数)を特定の場合に特化する必要がありますジェネリックプログラミングの場合、型システムを使用してさまざまなコードが「同じ」であることを認識し、特殊化のために型ベースのディスパッチを使用しようとします。

これらの2つの手法を一緒に使用して、かなり凝ったことを行うこともできます。この例については、ファンクターとモナドを使用した多段階プログラミング:ジェネリックコードからの抽象化オーバーヘッドの排除を参照してください。しかし、STLが怖いと思うなら、それを見ない方がいいです...

于 2010-06-17T01:07:01.510 に答える
3

いつ:

テンプレートメタプログラミングは、コンパイラによって解釈されるプログラムを作成する方法です。これは、1レベル上の抽象化です。あなたはそれであらゆる種類の奇妙でファンキーなことをすることができます。Boostライブラリには多くの例が含まれています。

  • C ++が静的に型付けされているのが気に入らないですか?boost::anyを使用します。
  • ラムダ計算が大好きですが、C ++である必要がありますか?boost:lambdaを使用します。
  • EBNFがあり、パーサーが必要ですか?boost::spiritを使用します。

なぜ:

かっこいいね。あなたはあなたの日付を印象づけることができます(多分)。

于 2009-06-11T11:04:18.697 に答える
1

かなり時間が残っていて、遊びたいとき。

アルゴリズムを強化する必要があり、仮想関数呼び出しによるオーバーヘッドが不要な場合は、ランタイムバインディングをコンパイル時バインディングに置き換えることができます。

于 2009-06-11T09:46:19.133 に答える
1

簡単な例:

template<typename T>
void
swap(T& var1, T& var2)
{
  T var3 = var1;
  var1 = var2;
  var2 = var3;
}

int a = 2;
int b = 3;
swap(a, b);

float c = 400.0f;
float d = 500.0f;
swap(c, d);

同じタイプの2つの変数の値を交換します。

テンプレートを使用すると、次のようなさまざまな型の組み合わせの関数を明示的に作成することを避けます。

void
swap(int& var1, int& var2)
{
  int var3 = var1;
  var1 = var2;
  var2 = var3;
}

void
swap(float& var1, float& var2)
{
  float var3 = var1;
  var1 = var2;
  var2 = var3;
}

上記の関数は、私の例では、swap()がintまたはfloat変数を使用してコード内のどこかで呼び出された場合、コンパイラーによって自動的に作成されます。

于 2009-06-11T09:51:13.083 に答える
0

ライブラリ開発者でない場合は、テンプレートのメタプログラミングを忘れた方がよいと思います。私は、それは本番コードでは役に立たず、より多くの問題を引き起こし、それから利益をもたらすと信じています。テンプレートは十分に柔軟ではないため、これは問題です。」.

PS もちろん、テンプレート コンテナー、スワップなどの意味ではありませんが、Alexandrescu のようなコードです

于 2009-06-11T09:53:53.640 に答える