C99 が関数のオーバーロードをサポートしないのには理由がありますか?

StackOverflow https://stackoverflow.com/questions/1314613

  •  19-09-2019
  •  | 
  •  

質問

どうやら(少なくともそれによると) gcc -std=c99) C99 は関数のオーバーロードをサポートしていません。C の新機能をサポートしない理由は通常、下位互換性のためですが、今回の場合、関数のオーバーロードによって下位互換性が損なわれるケースは思いつきません。この基本的な機能を含めない理由は何ですか?

役に立ちましたか?

解決

C でオーバーロードが発生する可能性が低い理由を理解するには、C++ でオーバーロードがどのように処理されるかをよく学ぶと役立つかもしれません。

コードをコンパイルした後、実行の準備が整う前に、中間 オブジェクトコード リンクする必要があります。これにより、コンパイルされた関数やその他のオブジェクトの大まかなデータベースが、すぐにロード/実行できるバイナリ ファイルに変換されます。この追加のステップは、コンパイルされたプログラムで利用できるモジュール性の原理メカニズムであるため、重要です。このステップにより、既存のライブラリからコードを取得し、それを独自のアプリケーション ロジックと組み合わせることができます。

この段階では、オブジェクト コードは任意の言語で、任意の機能の組み合わせで記述されている可能性があります。これを可能にするには、別のオブジェクトが参照するときにリンカが適切なオブジェクトを選択できるように、ある種の規則を設ける必要があります。アセンブリ言語でコーディングしている場合、ラベルを定義すると、何をしようとしているのかを理解していると想定されるため、そのラベルが正確に使用されます。

C では、関数はリンカのシンボル名になるため、次のように記述すると、

int main(int argc, char **argv) { return 1; }

コンパイラは、オブジェクト コードのアーカイブを提供します。このアーカイブには、と呼ばれるオブジェクトが含まれています。 main.

これはうまく機能しますが、リンカーはどちらの名前を使用するかを決定できないため、同じ名前を持つ 2 つのオブジェクトを持つことができないことを意味します。リンカーは引数の型については何も知りませんし、コード一般についてもほとんど知りません。

C++ では、追加情報をシンボル名に直接エンコードすることでこの問題を解決します。戻り値の型と引数の数と型はシンボル名に追加され、関数呼び出しの時点でそのように参照されます。リンカーが知る限り、関数呼び出しは明確であるため、リンカーはこれが起こっていることさえ認識する必要はありません。

この欠点は、シンボル名が元の関数名とまったく似ていないことです。特に、オーバーロードされた関数にリンクできるように、その関数の名前を予測することはほとんど不可能です。外部コードにリンクするには、次を使用できます extern "C", これにより、これらの関数は C スタイルのシンボル名に従いますが、もちろん、そのような関数をオーバーロードすることはできません。

これらの違いは、各言語の設計目標に関連しています。C は移植性と相互運用性を重視しています。C は、予測可能で互換性のあることを実行するために全力を尽くします。C++ は、リッチで強力なシステムの構築をより強く志向しており、他の言語との対話にはあまり重点を置いていません。

C では、C++ ほど操作が難しいコードを生成する機能を追求する可能性は低いと思います。

編集: イマジスト 尋ねます:

int main(int argc、char ** argv)をメインの代わりにmain-int-int-char **のようなものに解決した場合、関数と相互作用するのは本当に困難であるか、それは本当に難しいでしょうか(そしてこれはの一部でした標準)?ここに問題はないと思います。実際 これはあなたに与えるようです 詳細情報 ( 最適化などに)

これに答えるために、C++ とそれがオーバーロードを処理する方法に再び目を向けます。C++ はこのメカニズムをほぼ説明どおりに使用しますが、1 つ注意点があります。C++ は、C++ 自体の特定の部分をどのように実装するかを標準化しておらず、その省略がどのような結果をもたらすかを示唆しています。特に、C++ には、仮想クラス メンバーを含む豊富な型システムがあります。この機能をどのように実装するかはコンパイラ作成者に任されており、vtable の解決の詳細は関数のシグネチャに大きな影響を与えます。このため、C++ はコンパイラ作成者に対し、これらの主要な機能の実装が異なるコンパイラ間または同じコンパイラ間で名前マングリングが相互に互換性がないようにすることを意図的に提案しています。

これは、C++ や C のような高レベルの言語には詳細な型システムがある一方、低レベルのマシン コードにはまったく型がないという、より深刻な問題の症状にすぎません。任意のリッチ型システムは、マシン レベルで提供される型なしバイナリの上に構築されます。リンカーは、高水準言語で利用できる豊富な型情報にアクセスできません。リンカーは、すべての型抽象化を処理し、適切に型フリーのオブジェクト コードを生成するためにコンパイラーに完全に依存しています。

C++ は、マングルされたオブジェクト名に必要な型情報をすべてエンコードすることでこれを実現します。ただし、C は、ある種のポータブルなアセンブリ言語になることを目指しており、大きく異なります。したがって、C では、宣言された名前と結果のオブジェクトのシンボル名が厳密に 1 対 1 に対応することを好みます。C で名前がマングルされた場合、それが標準化された予測可能な方法であっても、変更された名前を目的のシンボル名に一致させるために多大な労力を費やす必要があり、そうでない場合は C++ の場合と同様にオフにする必要があります。C++ とは異なり、C の型システムはかなり小さくて単純であるため、この余分な労力はほとんど役に立ちません。

同時に、引数として受け取る型だけが異なる、似た名前の複数の C 関数を定義するのが実際には標準的な方法です。これに関する長い例については、以下をご覧ください。 OpenGL 名前空間。

他のヒント

あなたがCソースをコンパイルすると、

、シンボル名はそのまま残ります。あなたは関数のオーバーロードを導入する場合は、名前の衝突を防ぐために技術をマングリングネームを提供しなければなりません。そのため、C ++のように、あなたはマシンがコンパイルされたバイナリでシンボル名を生成しています。

また、Cは、厳密な型指定を備えていません。多くのものがオーバーロード解決ルールの複雑さは、言語のような種類の混乱を導入する可能性C.でお互いに暗黙的に変換されます。

私を含め、言語設計者の多くは、Cの暗黙のプロモーションとのオーバーロード関数の組み合わせが理解しheinously困難なコードにつながることができると思います。証拠のために、C ++についての蓄積された知識の体を見てください。

一般的に、C99は、既存の慣行とほぼ互換性のささやかな改正であることを意図していました。オーバーロードはかなり大きな出発されていると思います。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top