7

私はこの質問をするように私を導くリンクされた質問を読んでいました。

次のコードを検討してください

int main()
{
    string SomeString();
}

言うまでもなく、コンパイラはこれを文字列オブジェクトとしてではなく、関数プロトタイプとして受け取ります。ここで、次のコードについて考えてみます。

int main()
{
    string Some()
    {
        return "";
    }
}

入れ子関数の定義は許可されていないと思うので、コンパイラはこれは無効だと言いました。許可されていない場合、ネストされた関数プロトタイプが許可されるのはなぜですか?それは混乱を招くよりも利点を与えていません(または私はここでいくつかの有効なポイントを逃していますか?)。

以下が有効であることがわかりました。

int main()
{ 
  string SomeFun();
  SomeFun();
  return 0;
}

string SomeFun()
{
  std::cout << "WOW this is unexpected" << std::endl;
}

これも紛らわしいです。関数SomeFun()がmainにのみスコープを持つことを期待していました。しかし、私は間違っていました。なぜコンパイラは上記のようなコードをコンパイルできるのですか?上記のようなコードが理にかなっているリアルタイムの状況はありますか?

何かご意見は?

4

6 に答える 6

8

プロトタイプは「前方宣言」です。ウィキペディアの記事をチェックしてください。

基本的には、「ラベル「SomeFun」がこのように使用されても心配しないでください」とコンパイラーに指示します。 ただし、リンカは正しい関数本体を見つける責任があります。

実際には、「char SomeFun()」などの偽のプロトタイプを宣言して、メイン全体で使用できます。リンカが偽の関数の本体を見つけようとしたときにのみエラーが発生します。しかし、あなたのコンパイラはそれでかっこいいでしょう。

たくさんのメリットがあります。関数本体が常に同じソースコードファイルにあるとは限らないことを覚えておく必要があります。リンクライブラリに含めることもできます。また、そのリンクライブラリには特定の「リンク署名」が含まれる場合があります。条件付き定義を使用すると、スコープ付きプロトタイプを使用してビルド時に正しいリンク署名を選択することもできます。ただし、ほとんどの人は関数ポインタを使用します。代わりにそれ。

お役に立てれば。

于 2009-05-30T04:42:18.130 に答える
7

補足として、C++03 にはローカル関数を定義する回り道があります。local-class 機能を悪用する必要があります。

int main()
{
    struct Local
    {
        static string Some()
        {
            return "";
        }
    };
    std::cout << Local::Some() << std::endl;
}
于 2009-05-30T09:38:19.507 に答える
5

これは、C++が採用している多くのCの規則です。

Cの別の関数内で関数を宣言する機能は、ほとんどのプログラマーがおそらく残念で不必要だと考える決定です。特に、関数定義がCよりも比較的小さい最新のOOP設計では。

別の関数のスコープ内にのみ存在する関数が必要な場合は、boost::lambdaC++1xlambdaの2つのオプションがあります。

于 2009-05-30T04:35:50.417 に答える
5

なぜあなたの宣言の理由について

void f() {
    void g(); g();
}

これよりも良いです

void g();
void f() {
    g();
}

一般に、宣言をできるだけローカルに保持して、名前の競合ができるだけ少なくなるようにすることをお勧めします。関数をローカルで (このように) 宣言することが本当に幸運であるかどうかは議論の余地があると思います. 通常はそのヘッダーをインクルードしてから「通常の」方法で進む方が良いと思います. 場合によっては、シャドウ関数を回避することも役立ちます

void f() {
    int g; 
    // oops, ::g is shadowed. But we can work around that
    {
        void g(); g();
    }
}

もちろん、C++ では-gを使用して関数を呼び出すことができits_namespace::g()ましたが、昔の C ではそれは不可能であり、プログラマーは依然として関数にアクセスすることができました。また、構文的には同じではありませんが、意味的には、以下は実際には異なるスコープをターゲットとするローカルスコープ内の関数も宣言していることに注意してください。

int main() {
    using std::exit;
    exit();
}

余談ですが、宣言のターゲット スコープが、その宣言が表示されるスコープではないような状況が他にもあります。一般に、宣言したエンティティは、宣言が表示されるスコープのメンバーになります。しかし、常にそうであるとは限りません。たとえば、そのことが起こる友人宣言を考えてみましょう

struct X { friend void f() { std::cout << "WoW"; } };
int main() { void f(); f(); } // works!

の関数宣言 (および定義!) はfのスコープ内で行われましたがX、エンティティ (関数自体) は外側の名前空間のメンバーになりました。

于 2009-05-30T15:12:44.730 に答える
3

関数プロトタイプは、コンパイラーのヒントです。これらは、関数がまだ検出されていない場合でも、別の場所に実装されていることを示しています。これ以上何もない。

于 2009-05-30T04:42:26.057 に答える
3

あなたが行っているようにプロトタイプを宣言するとき、基本的にはリンカーがそれを解決するのを待つようにコンパイラーに伝えています。プロトタイプを作成する場所に応じて、スコープ ルールが適用されます。main() 関数内にプロトタイプを記述することは技術的に間違っているわけではありません (IMHO は少し面倒ですが)。これは、関数が main() 内でローカルにのみ認識されることを意味します。ソース ファイルの先頭 (またはより一般的にはヘッダー ファイル) でプロトタイプを宣言した場合、プロトタイプ/関数はソース全体で認識されます。

string foo()
{
  string ret = someString();  // Error
  return ret; 
}

int main(int argc,char**argv)
{
   string someString();
   string s = somestring(); // OK
   ...
}
于 2009-05-30T08:07:06.090 に答える