この質問にはすでに回答があります:
コメント
回答
ロバートの回答でで説明されているvoid *
ポインタのほかに、このような手法が使用されました(免責事項: 20年前のメモリ):
#define WANTIMP #define TYPE int #include "collection.h" #undef TYPE #define TYPE string #include "collection.h" #undef TYPE int main() { Collection_int lstInt; Collection_string lstString; }
collection.h
内の正確なプリプロセッサの魔法を忘れたところですが、このようなもの:
class Collection_ ## TYPE { public: Collection_ ## TYPE () {} void Add(TYPE value); private: TYPE *list; size_t n; size_t a; } #ifdef WANTIMP void Collection_ ## TYPE ::Add(TYPE value) #endif
コメント
回答
ジェネリックを使用せずにジェネリックを実装する従来の方法(テンプレートが作成された理由)は、voidポインターを使用することです。
typedef struct Item{ void* data; } Item; typedef struct Node{ Item Item; struct Node* next; struct Node* previous; } Node;
このサンプルコードでは、バイナリツリーまたは二重にリンクされたリストを表すことができます。 item
はvoidポインターをカプセル化するため、任意のデータ型をそこに格納できます。もちろん、使用可能なオブジェクトにキャストして戻すには、実行時にデータ型を知っている必要があります。
コメント
回答
他の回答が指摘しているように、一般的なデータ構造にはvoid*
を使用できます。他の種類のパラメトリックポリモーフィズムでは、何かがロット(数十回など)繰り返された場合にプリプロセッサマクロが使用されました。ただし、正直なところ、適度な繰り返しの場合、マクロには問題となる落とし穴がたくさんあるため、コピーして貼り付けてからタイプを変更することがほとんどです。
本当に必要なのは blub paradox の逆の名前。このサイトでは多くのことが取り上げられているため、表現力の低い言語でのプログラミングを想像するのに苦労しています。パラメトリックポリモーフィズムを実装する表現力豊かな方法で言語を使用したことがない場合は、何が欠けているのか本当にわかりません。コピーと貼り付けをやや面倒ですが、必要なものとして受け入れるだけです。
現在選択している言語には、まだ気付いていない非効率性があります。 20年後、人々はあなたがそれらをどのように排除したのか疑問に思うでしょう。簡単に言うと、「できなかった」ということです。
コメント
回答
gccがgenclass
に同梱されていたときのことを覚えています-一連のパラメータータイプを入力として受け取ったプログラム(たとえば、マップのキーと値)と、パラメータ化されたタイプ(たとえば、マップまたはベクトル)を記述し、パラメータタイプが入力された有効なC ++実装を生成する特別な構文ファイル。
Map<int, string>
とMap<string, string>
が必要でした(これは実際の構文ではありませんでした)。そのプログラムを2回実行して、map_string_string.hやmap_int_string.hなどを生成し、コードで使用します。
iv idについては、のマニュアルページを参照してください。詳細については、= “ebaedd2ca2″>
および GNU C ++ライブラリ2.0のドキュメントを参照してください。
回答
[OPへ:私はあなたを個人的に選ぶのではなく、あなたや他の人に質問の論理について考える意識を高めます( s)SEおよび他の場所で尋ねた。 「これを個人的に受け取らないでください!]
質問のタイトルは適切ですが、「…コンパイル時のコード生成が必要な状況」を含めることで、回答の範囲を大幅に制限しています。 「このページには、テンプレートを使用せずにC ++でコンパイル時のコード生成を行う方法に関する質問に対する多くの良い答えがありますが、最初に提起した質問に答えるために:
C ++のテンプレートの前に人々は何をしましたか?
答えは、もちろん、彼ら(私たち)はそれらを使用しなかったということです。はい、私は冗談を言っていますが、本文の質問の詳細は、(おそらく誇張されて)誰もがテンプレートを愛し、テンプレートなしではコーディングを行うことができなかったと想定しているようです。
例として、コンパイル時のコード生成を必要とせずに、さまざまな言語で多くのコーディングプロジェクトを完了しましたが、他の人もそうだと信じています。確かに、テンプレートによって解決された問題は、誰かが実際にそれを引っ掻くほどの大きさのかゆみでしたが、この質問によって提起されたシナリオは、ほとんど存在しませんでした。
車で同様の質問を検討してください:
オートマチックトランスミッションが発明される前に、ドライバーはどのようにしてギアをシフトする自動化された方法を使用して、あるギアから別のギアにシフトしましたか?
もちろん、問題はばかげています。 Xが発明される前に人がXをどのように行ったかを尋ねることは、実際には有効な質問ではありません。答えは、一般に、「それが存在することを知らなかったので、私たちはそれをしなかったし、見逃しませんでした」です。はい、事後にメリットを確認するのは簡単ですが、全員が立っていたり、かかとを蹴ったり、オートマチックトランスミッションを待ったり、C ++テンプレートを待っていたりすることは実際には真実ではありません。
「オートマチックトランスミッションが発明される前に、ドライバーはどのようにギアをシフトしたのか」という質問に対して、「手動で」と合理的に答えることができます。それが、ここで得られるタイプの答えです。それはあなたが尋ねようとしていたタイプの質問でさえあるかもしれません。
しかし、それはあなたが尋ねたものではありませんでした。
だから:
Q:テンプレートが発明される前に人々はどのようにテンプレートを使用しましたか?
A:私たちはしませんでした。
Q:テンプレートが発明される前に人々はどのようにテンプレートを使用しましたか?必要なときにテンプレートを使用する?
A:テンプレートを使用する必要はありませんでした。 使用したと想定するのはなぜですか?(使用するのはなぜですか?)
Q:テンプレートが提供する結果を達成するための代替方法は何ですか?
A:上記には多くの良い答えがあります。
投稿する前に、投稿の論理的誤謬について考えてください。
[ありがとうございます。ここでは害はありません。]
コメント
答え
回答
Robert Harveyがすでに述べたように、voidポインタは汎用データ型です。
標準Cライブラリの例で、doubleの配列を汎用ソートでソートする方法:
double *array = ...; int size = ...; qsort (array, size, sizeof (double), compare_doubles);
compare_double
の定義:
int compare_doubles (const void *a, const void *b) { const double *da = (const double *) a; const double *db = (const double *) b; return (*da > *db) - (*da < *db); }
qsort
の署名はstdlib.hで定義されています:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *) );
タイプがないことに注意してください実行時ではなく、コンパイル時にチェックします。ダブルスを期待する上記のコンパレータを使用して文字列のリストを並べ替えると、文字列のバイナリ表現をダブルとして解釈し、それに応じて並べ替えようとします。
回答
これを行う1つの方法は、次のとおりです。
https://github.com/rgeminas/gp–/blob/master/src/scope/darray.h
#define DARRAY_DEFINE(name, type) DARRAY_TYPEDECL(name, type) DARRAY_IMPL(name, type) // This is one single long line #define DARRAY_TYPEDECL(name, type) \ typedef struct darray_##name \ { \ type* base; \ size_t allocated_mem; \ size_t length; \ } darray_##name; // This is also a single line #define DARRAY_IMPL(name, type) \ static darray_##name* darray_init_##name() \ { \ darray_##name* arr = (darray_##name*) malloc(sizeof(darray_##name)); \ arr->base = (type*) malloc(sizeof(type)); \ arr->length = 0; \ arr->allocated_mem = 1; \ return arr; \ }
DARRAY_TYPEDECLマクロは、name
と、渡したtype
の配列を格納します(name
は、連結できるようにあります。 DARRAY_IMPLマクロは、その構造体を操作する関数を定義します(この場合、静的とマークされます)。定義を1回だけ呼び出し、すべてを分離しないでください。
これは次のように使用されます:
#include "darray.h" // No types have been defined yet DARRAY_DEFINE(int_ptr, int*) // by this point, the type has been declared and its functions defined darray_int_ptr* darray = darray_int_ptr_init();
回答
テンプレートは、次のようなコンテナタイプを再利用する方法としてよく使用されると思います。動的配列(ベクトル)、マップ、ツリーなどの多くのアルゴリズム値の並べ替えなど。
テンプレートがない場合、必然的に、これらの実装には一般的な方法で記述され、十分な情報が提供されます。ドメインに必要なタイプについて。たとえば、ベクターの場合、データがblt “可能であり、各アイテムのサイズを知っている必要があります。
これを行うVectorというコンテナクラスがあるとします。ボイド*がかかります。これの単純な使用法は、アプリケーション層コードが多くのキャストを行うことです。したがって、Catオブジェクトを管理している場合は、Cat *をvoid *にキャストし、あちこちに戻す必要があります。キャストを使用してアプリケーションコードをポイ捨てすることには明らかな問題があります。
テンプレートはこれを解決します。
これを解決する別の方法は、コンテナに格納しているタイプのカスタムコンテナタイプを作成することです。したがって、Catクラスがある場合は、Vectorから派生したCatListクラスを作成します。次に、使用するいくつかのメソッドをオーバーロードし、void *の代わりにCatオブジェクトを使用するバージョンを導入します。したがって、Vector :: Add(void *)メソッドをCat :: Add(Cat *)でオーバーロードします。これは、内部的にパラメーターをVector :: Add()に渡すだけです。次に、アプリケーションコードで、 Catオブジェクトを渡すときにAddのオーバーロードされたバージョンであるため、キャストを回避します。公平を期すために、Cat *オブジェクトはキャストなしでvoid *に変換されるため、Addメソッドはキャストを必要としません。ただし、インデックスのオーバーロードやGet()メソッドなどのアイテムを取得するメソッドは必要です。
他のアプローチは、90年代初頭から大規模なアプリケーションフレームワークで思い出した唯一の例で、クラスに対してこれらの型を作成するカスタムユーティリティを使用することです。MFCがこれを行ったと思います。コンテナには異なるクラスがありました。 CStringArray、CRectArray、CDoulbeArray、CIntArrayなどのように。重複したコードを維持するのではなく、クラスを生成する外部ツールを使用して、マクロと同様のある種のメタプログラミングを行いました。使いたかった-使ったことがなかった。たぶん持っていたはずだった。でも当時、専門家たちは「C ++のまともなサブセット」と「テンプレートは必要ない」と宣伝していた