1

私は現在プログラミング言語を設計しており、この問題を解決する方法に興味があります:

次のようなクラス (またはインターフェイス) A があるとします。

class A { // size is 4 bytes
  int32 a = 0;
}

それを拡張し、次のように見える2番目のクラス B:

class B extends A { // size is 8 bytes
  int32 b = 0;
}

そして、次のような関数 f があるとします。

int32 f(A first, A second) {
  return first.a + second.a;
}

ただし、2 つの B で呼び出すと、second.a は 2 つの As で呼び出された場合と同じ場所にはなりません。これは、最初のパラメーターがそれをシフトするためです。これを解決するための私の現在の考えは次のとおりです。

  1. 不明なサイズのパラメーターを許可せず、ポインターまたは参照として渡すように強制します (これが Rust の機能だと思います)。
  2. 次のすべての情報をコール スタックに書き込みます。
  3. first と second の可能なサイズごとに関数を作成し、既知の場合はコンパイル時、または vtables を使用して実行時にどちらを呼び出すかを決定します。

2 番目のアイデアは、サブタイプを使用してほとんどまたはまったく呼び出されない場合でも、すべての関数でサポートされる必要があるため、問題になります。これは非効率的です。

3 番目のアイデアでは、多くの関数を作成する必要があります (20 の異なるサブタイプを持つことができる 5 つのパラメーターを受け入れる関数は、未知の型のパラメーターで 1 つだけ呼び出された場合、100 個の同様のコードを生成する必要があります)。それを使用する関数が 1 つだけあるクラスごとに vtable が必要になります。また、コンパイル済みのライブラリ内の関数は、新しいサブタイプでは使用できませんでした。

2 と 3 を組み合わせて、同じ関数の 2 つのバージョンを作成します。1 つは型のみを受け入れ、もう 1 つはサブタイプも受け入れるものです。これらの問題のいくつかは解決できます。

これに対するより良い解決策があるかどうか、および C++ などの他の言語がこれをどのように実装しているかに興味があります。

4

1 に答える 1

2

C++ では、値によるf(A)サブタイプのパラメーターでの呼び出しは、B

f(static_cast<const A&>(b));

これstatic_castにより、同じアドレスのメモリがより短いデータ ブロックの開始として再解釈されるか、最初にオフセットが透過的に追加される可能性があります (Aが最初の基本クラスではないか、仮想である場合)。その後、内部的に、 のコピー コンストラクターAが呼び出されます。どちらの場合でも、によって追加された情報はB、仮想関数のオーバーライドとともに完全に失われます。すべての目的のために、渡されるものはもはやB.

動的ポリモーフィズムには、概説した理由から、参照またはポインターが必要です。しかし、「値による参照」を渡したい場合、最も簡単な解決策はおそらくオブジェクトのコピーへの参照を渡すことでしょう。このような場合、各オブジェクトは、正しいコピー コンストラクターを呼び出すか、共通のスーパークラスから派生してclone().

于 2016-12-22T19:18:22.367 に答える