23

この質問に対するいくつかの回答を読んだとき、コンパイラが最初に関数に遭遇したときに、なぜ実際に関数について知る必要があるのか​​疑問に思い始めました内部で宣言されたすべてのシンボルを収集するコンパイルユニットを解析するときに、パスを追加するだけで、宣言および使用される順序が重要でなくなるのは簡単ではないでしょうか。

関数を使用する前に宣言するのは確かに良いスタイルであると主張する人もいるかもしれませんが、C ++でこれが必須である理由は他にあるのでしょうか?

編集-説明する例:ヘッダーファイルでインラインで定義されている関数が必要だとします。これらの2つの関数は、相互に呼び出します(おそらく、ツリーの奇数層と偶数層が異なる方法で処理される再帰的なツリー走査)。これを解決する唯一の方法は、一方の関数をもう一方の関数の前に前方宣言することです。

より一般的な例(関数ではなくクラスを使用)は、privateコンストラクターとファクトリを使用するクラスの場合です。ファクトリは、そのインスタンスを作成するためにクラスを知る必要があり、クラスはfriend宣言のためにファクトリを知る必要があります。

これが昔からの要件である場合、なぜある時点で削除されなかったのですか?既存のコードを壊すことはありませんね。

4

11 に答える 11

12

別の翻訳単位で定義されている宣言されていない識別子をどのように解決することを提案しますか?

C++ にはモジュールの概念はありませんが、C からの継承として別個の変換があります。C++ コンパイラは、他の翻訳単位について何も知らずに、各翻訳単位を単独でコンパイルします。(それがこれを壊したことを除いてexport、それがおそらく悲しいことに、それが決して離陸しなかった理由です。)
ヘッダーファイルは、通常、他の翻訳単位で定義されている識別子の宣言を置く場所ですが、実際には同じものを滑らせる非常に不器用な方法です異なる翻訳単位への宣言。それらは、識別子が定義されている他の翻訳単位があることをコンパイラに認識させません。

追加の例を編集
してください: 適切なモジュールの概念の代わりにすべてのテキストを含めると、コンパイルはすでに C++ で非常に長い時間がかかるため、別のコンパイル パスが必要です (コンパイルはすでにいくつかのパスに分割されており、そのすべてを最適化してマージできるわけではありません)。 、IIRC) は、すでに悪い問題を悪化させます。また、これを変更すると、シナリオによってはオーバーロードの解決が変更され、既存のコードが壊れる可能性があります。

クラス定義でインラインで定義されたメンバー関数は、クラス定義のすぐ後ろで定義されているかのように解析されるため、C++ ではクラス定義を解析するための追加のパスが必要であることに注意してください。ただし、これは C with Classes が考え出されたときに決定されたため、既存のコード ベースを壊す必要はありませんでした。

于 2011-01-21T10:41:21.923 に答える
10

歴史的に、C89 ではこれが可能でした。コンパイラが関数の使用を初めて確認し、事前定義されたプロトタイプがない場合、関数の使用に一致するプロトタイプを「作成」しました。

C++ が厳密な型チェックをコンパイラに追加することを決定したとき、プロトタイプが必要になったことが決定されました。また、C++ は C からシングルパス コンパイルを継承しているため、すべてのシンボルを解決するために 2 番目のパスを追加できませんでした。

于 2011-01-21T15:24:37.167 に答える
9

C と C++ は古い言語だからです。初期のコンパイラには多くのメモリがありませんでした。そのため、これらの言語は、ファイル全体を考慮する必要なく、コンパイラがファイルを上から下に読み取ることができるように設計されていました。

于 2011-01-21T10:41:02.997 に答える
4

私は2つの理由を考えています:

  • これにより、解析が容易になります。余分なパスは必要ありません。
  • また、スコープも定義します。シンボル/名前は、その宣言後にのみ使用できます。 つまり、グローバル変数を宣言すると、この行の後のコードはそれを使用できますが、行の前のコードは使用できません! グローバル関数の同じ引数。int g_count;

例として、次のコードを検討してください。

void g(double)
{
    cout << "void g(double)" << endl;
}
void f()
{
    g(int());//this calls g(double) - because that is what is visible here
}
void g(int)
{
    cout << "void g(int)" << endl;
}
int main()
{
    f();
    g(int());//calls g(int) - because that is what is the best match!
}

出力:

void g(double)
void g(int)

ideone の出力を参照してください: http://www.ideone.com/EsK4A

于 2011-01-21T10:34:32.623 に答える
2

主な理由は、コンパイルプロセスを可能な限り効率的にすることです。余分なパスを追加すると、時間とストレージの両方が追加されます。C++ はクアッド コア プロセッサの時代よりも前に開発されたことを思い出してください :)

于 2011-01-21T10:33:46.470 に答える
2

C プログラミング言語は、コンパイラをワンパス コンパイラとして実装できるように設計されました。このようなコンパイラでは、各コンパイル フェーズは 1 回だけ実行されます。このようなコンパイラでは、ソース ファイルで後で定義されたエンティティを参照できません。

さらに、C では、コンパイラは一度に 1 つのコンパイル単位 (通常は .c ファイルと含まれるすべての .h ファイル) のみを解釈します。したがって、別のコンパイル単位で定義された関数を参照するメカニズムが必要でした。

ワンパス コンパイラを許可し、プロジェクトを小さなコンパイル ユニットに分割できるようにするという決定が下されたのは、当時、利用可能なメモリと処理能力が非常に逼迫していたからです。また、前方宣言を許可すると、単一の機能で問題を簡単に解決できます。

C++ 言語は C から派生し、C から機能を継承しました (移行を容易にするために、可能な限り C との互換性を保つ必要があったため)。

于 2011-01-21T10:47:26.320 に答える
1

C はかなり古く、C が設計された当時、CPU がはるかに遅かったため、効率的なコンパイルが問題だったからだと思います。

于 2011-01-21T10:37:41.923 に答える
1

C++ は静的言語であるため、コンパイラは、値の型が関数のパラメーターで期待される型と互換性があるかどうかを確認する必要があります。もちろん、関数のシグネチャがわからない場合、この種のチェックを行うことはできず、静的コンパイラの目的に反することになります。しかし、あなたは C++ のシルバー バッジを持っているので、このことは既に知っていると思います。

C++ 言語仕様は、ハードウェアが現在入手可能なものほど高速ではなかったときに、設計者がマルチパス コンパイラを強制することを望まなかったため、適切に作成されました。最終的には、今日 C++ が設計されていれば、この押し付けはなくなりますが、別の言語が必要になるでしょう :-)。

于 2011-01-21T10:39:21.740 に答える
0

それでも、宣言される前に関数を使用できる場合があります (厳密に言えば、「前」とはプログラム ソースが読み込まれる順序に関するものです) -- クラス内で!:

class A {
public:
  static void foo(void) {
    bar();
  }
private:
  static void bar(void) {
    return;
  }
};

int main() {
  A::foo();
  return 0;
}

(私のテストによると、クラスを名前空間に変更しても機能しません。)

これはおそらく、誰かが回答で指摘したように、コンパイラが実際にクラス宣言の直後にクラス内からメンバー関数定義を配置するためです。

同じアプローチをソース ファイル全体に適用することもできます。まず、宣言以外のすべてを削除し、次に延期されたすべてを処理します。(2 パス コンパイラ、または延期されたソース コードを保持するのに十分な大きさのメモリのいずれか。)

ハハ!そのため、ソースファイル全体は大きすぎてメモリに保持できないと考えていましたが、関数定義を持つ単一のクラスはそうではありません。クラス全体をメモリに保持し、宣言が除外されるまで待機することができます (または、クラスのソース コードに対して 2 回目のパスを実行します)。

于 2011-01-29T04:11:50.423 に答える
-1

GlobalUnix と Linux のことを覚えていますLocal。独自の環境内では local は関数に対して機能しますが、 に対しては機能しませんGlobal(system)。関数を宣言する必要がありますGlobal

于 2017-01-27T06:39:02.667 に答える