web-dev-qa-db-ja.com

C ++のmain()をインライン化できないのはなぜですか?

C++のFAQを読んでいて、1つの文に気づきました。

main()をインラインにすることはできません。

どうしてこれなの?

69
jon

C++では、コード内でmain関数を呼び出すことは合法ではないため、インライン化する方法はありません。

104
sepp2k

標準がそう言っているので:

_[2003: 3.6.1/3]_:関数mainは、プログラム内では使用されません(3.2)。 mainのリンケージ(3.5)は実装定義です。 mainをインラインまたは静的であると宣言するプログラムは形式が正しくありません。 mainという名前は他の方法で予約されていません。 [例:他の名前空間のエンティティと同様に、メンバー関数、クラス、および列挙型をmainと呼ぶことができます。 ]

そして、なぜそれはそう言うのですか? mainの実装については、個人にできるだけ多く任せようとしているので、implementation..可能な限り、そしておそらく実際的なメリットがない場合に、ここでinlineが有効であることを要求することにより、実装を制限したくありません。


委員会の私の友人はこれを確認しました:

inlinemain()自体が機能しない理由はありません。 [..]インライン化されたmain()を呼び出すことができるC++インタープリターを持つことができます。 [..] [しかし] inline/staticmain()は、混乱を避けるために禁止されています。 [このQ&A]ですでに述べられていることに理論的根拠が追加されるとは想像しがたいです。


ところで、inlineヒントキーワードを実際のインライン関数と混同しないでください。関数にinlineのマークを付けることができますが、物理的にインライン化されていない可能性があります。

したがって、main "をインライン化できない"(厳密に言えば、nottrueではありませんが、mainは、他の回答で説明されているように、かなり厄介で無意味です)、理論的には、inlineヒントキーワードを問題なくサポートできます。

それは上記の理由ではなく、litbの答えでは、実際の利益のために問題を複雑にするでしょう。

Cランタイムライブラリは、実行する関数を「知る」ために、このシンボルを見つける必要があります。

27
Ringding

Main()を直接呼び出すことはできないため(c ++では禁止されています)、インライン化する意味はありません。

17
Karoly Horvath

通常、main()はシステムのinit()関数から呼び出されます。したがって、main()には正確に1つの定義が存在する必要があります。

ここで、inlinemain()関数をヘッダーファイルにインクルードできる場合、翻訳単位ごとにmain()の定義が異なります。これは許可されていません。 namespaceinlinemain()を宣言できます。しかし、グローバルmain()ではありません。

14
iammilind

まず、インラインで仕事関数がどのように機能するかを理解する必要があります

例:

 inline void f() {
     int a  = 3;
     a += 3;
     cout << a;
 }

 int main() {
      f();
      return 0;
 }

コンパイラには次のようになります。

 int main() {
        int a  = 3;
        a += 3;
        cout << a;
        return 0;
 }

この例を見て、どのようにメインをインラインにしますか?このメソッドはすぐにインラインになります。

10
nirmus

C++標準では、@ Tomalak Geret'kalの応答に従って、main関数をインライン化することはできないとされています。この応答は、標準の制限が削除された場合に、main関数のインライン化の可能性について説明しています。

インラインの定義
inlineキーワードは、関数の内容をその場で貼り付けるためのコンパイラーへの提案です。 1つの目的は、関数(サブルーチン)の呼び出しと戻りに存在するオーバーヘッドを取り除くことです。

インライン化の重要な状況は、関数へのポインターがある場合です。この場合、関数の静的コピーが少なくとも1つ存在する必要があります。この場合、静的バージョンが1つあるため、リンカーはインライン関数の「外部リンケージ」を解決できます。

コンパイラとリンカは、コンテンツを貼り付けるか、関数の単一インスタンスを呼び出すかを決定することに注意してください。

また、プログラマーによってタグ付けされていない関数は、コンパイラーによってインライン化される場合もあります。

メイン関数のインライン化
許可されるmainの呼び出しは1つだけなので、howリンクされるのはコンパイラー次第です。インライン関数の単一インスタンスは、標準で許可されています。コンパイラは、inlined関数を単一インスタンスへの関数呼び出しに変換できます。したがって、コンパイラはignoremain関数のインライン提案になります。

コンパイラとリンカは、インライン化されたmain関数のインスタンスが1つだけ存在することを保証する必要があります。これは、特に外部リンケージの場合に注意が必要な部分です。 1つのインスタンスを確保するための1つのプロセスは、インライン化されているかどうかに関係なく、翻訳に「メイン」機能があるという情報を残すことです。 注:インライン関数が呼び出されると、コンパイラは外部リンケージのシンボルテーブルから関数を削除できます。これは、関数がによって呼び出されないという考え方があるためです。外部関数。

概要
技術的にmain関数がインライン化されるのを妨げるものは何もありません。 machineryは、インライン関数を単一のインスタンスに変換し、関数の複数のインスタンスを識別するためにすでに存在します。インライン関数へのポインタがある場合、関数のインスタンスが1つ作成されるため、アドレスがあります。この機構は、アドレスを持つmainのランタイムライブラリ要件を満たします。 inline関数のmainの場合、無視されますが、この構文を妨げる理由はありません(混乱する人を除く)。結局のところ、値(コピー)によって渡されるパラメーターをconstとして宣言するなど、冗長な構文ケースがすでに存在します。

「それは私の意見です、私は間違っている可能性があります。」 -デニスミラー、コメディアン。

7
Thomas Matthews

mainの呼び出しは、マシンコードレベルで意味のあるインライン化ができないと指摘する人もいます。それはごみです。リンカからの少しの助け(グローバル最適化など)またはランタイムライブラリのビットのアプリケーションごとの再コンパイルが必要になりますが、それはかなり実行可能であり、ここでは技術的な問題はありません。

ただし、inlineの-​​ヒント効果は、呼び出しをインライン化することが望ましいため、mainです。

inlineの唯一の保証された効果は、外部リンケージ関数を2つ以上の変換単位で(同一に)定義できるようにすることです。つまり、単一定義規則に影響を与えます。

実際問題として、これにより定義をヘッダーファイルに配置できます。また、同一の定義を保証するには、定義をヘッダーファイルに配置することも実際に必要です。

これはmainには意味がないため、maininlineである理由はありません。

mainを定義できるのは1回だけです。したがって、inlineを置くことは何の目的にも役立ちません-inlineは、プログラムで複数回定義できる関数に対してのみ重要な目的を持っています(すべての定義は、1つの定義とすべてが存在するかのように扱われます定義は同じである必要があります)。

inline関数はプログラム内で複数回定義でき、inlineは、inlineでマークされた関数をできるだけ速く呼び出す目的も果たすため、標準ではinline関数は、それが使用されるすべての変換単位で定義されます。したがって、コンパイラーは通常、関数の定義がinlineであり、その関数が現在の変換単位のコードによって使用されていない場合、その定義を破棄します。 mainに対してこれを行うことは完全に間違っています。これは、inlinemainのセマンティクスが完全に互換性がないことを示しています。

タイトルの「C++のmain()をインライン化できないのはなぜですか?」という質問に注意してください。そして、あなたが標準から引用する声明は、さまざまなことに関係しています。関数をインライン化できるかどうかを尋ねています。これは、呼び出された関数のコードを呼び出し元の関数に完全にまたは部分的に挿入すると一般に理解されています。関数にinlineをマークするだけでは、その関数をインライン化することはまったく意味しません。これは完全にコンパイラの決定であり、もちろん、mainを呼び出さない場合(そして呼び出すことができない場合)、インライン化するものは何もありません。

CRTに静的にリンクした場合およびリンク時のコンパイルインライン化(​​MSVCのように)を有効にすると、インライン化できる可能性があります。

しかし、それは本当に意味がありません。これはonceと呼ばれ、mainの最初の行が実行される前に実行される他のすべてと比較して、その関数の呼び出しオーバーヘッドは実質的にありません。

.。

Aaand、実行可能ファイルにシンボルを1回だけ表示するのは簡単な方法です。 :)

3
Macke

基本的な理由はいくつかあります。基本的に、mainは、ランタイムの基本的な初期化ルーチンから呼び出され、そこからのみ呼び出されます。そのコードは(明らかに)あなたのmainがインライン化されていることを知らずにコンパイルされました。最新のコンパイラテクノロジは、モジュールの境界を越えてインライン化できますが、高度な機能であり、多くの古いコンパイラではサポートされていません。そしてもちろん、インライン化の利点は、関数が非常に頻繁に呼び出される場合にのみ存在します。定義上、mainは1回だけ呼び出され、それ以上でもそれ以下でもありません。

2
James Kanze

標準ではそう言っているようですが、実際の実際の答えは、すべてのCおよびC++プログラムに追加されたランタイムが実行可能ファイルのあるポイントを呼び出さなければならないと述べるのと同じくらい簡単です。その関数には、実行の開始時に呼び出される関数をリンカーが検出できるように、外部シンボル(および実行時のアドレス)が必要です。したがって、インライン化されたコンパイラは外部シンボルを生成しないため、inlineとして宣言することはできません。

2
Diego Sevilla

コンパイラ/アーキテクチャのほとんどの組み合わせでは、ソースのmain()関数は、最終的なバイナリではかなり正常な関数になります。これは、それらのアーキテクチャで便利であるという理由だけであり、標準でそうしなければならないと言われているからではありません。

メモリに制約のあるアーキテクチャでは、動的リンカーフレンドリーコンテナ(elfやxcoffなど)の代わりにフラットバイナリ(intex hex形式など)を生成する多くのコンパイラが、ボイラープレートをすべて最適化します。一部のアーキテクチャは関数呼び出しをまったくサポートしていません(これらのプラットフォームではC++の限られたサブセットのみが可能です)。

このようなさまざまなアーキテクチャとビルド環境をサポートするために、標準の選択では、main()のセマンティクスを可能な限りオープンに保ち、コンパイラがさまざまなプラットフォームに適切な処理を実行できるようにします。つまり、言語全体で利用できる多くの機能は、アプリケーション自体の起動とシャットダウンには適用できません。

インラインmain()(または再入可能性、またはその他の高度な機能)のようなものが必要な場合は、もちろん、メイン関数を別の何かと呼ぶことができます。

inline int myMain(int argc, char **argv) { /* whatever */ }
int main(int argc, char **argv) { return myMain(argc, argv); }

インライン関数はデフォルトで静的スコープを持っています。 main()をインラインとして宣言すると、そのスコープは定義されているファイルに限定されます。ただし、Cスタートアップライブラリ(コンパイラベンダーが提供)は、「main」がグローバルシンボルである必要があります。リンカフラグを使用してエントリポイント関数(mainなど)を変更できるコンパイラがいくつかあります。

1
Venki

実行を開始するmain()関数なので、コードがバイナリにコンパイルされると、すべてがmain()自体に含まれます。つまり、すでにインライン化されていると言えます。

はい、C++プログラムにインラインを使用することは違法です。それは構文に関することです!

1
linuxeasy

インライン関数には通常アドレスがないため、mainを呼び出すポータブルな方法はありません。main()には、initコードがジャンプできるアドレスが必要です。インライン化された関数は、呼び出し元の関数に固定されることを意図しています。mainがインライン化されている場合は、プログラムのinitコードにインライン化する必要があります。これも移植性がありません。

0
Santosh