9

.oファイルをリンクするだけで関数を呼び出す方法はありますか?

例えば:

foo.cpp:

extern int x;

void f() { x = 42; }

struct T { T() { f(); } } t; // we use constructor of global
                             // object to call f during initialization

bar.cpp:

#include <iostream>

int x;

int main()
{
    std::cout << x;
}

コンパイル/リンク/実行するには:

$ g++ -c foo.cpp
$ g++ -c bar.cpp
$ g++ foo.o bar.o
$ ./a.out
42

これはgcc4.7で動作するようです。期待通り42を出力します。ただし、一部の古いコンパイラでは、foo.oを実際に「使用」していないため、リンク時に最適化されるというこのパターンの問題が発生したことを覚えています。(おそらく、この特定の例は、何らかの理由で問題を表していない)

C ++ 11標準はこのパターンについて何と言っていますか?動作することが保証されていますか?

4

3 に答える 3

8

私はあなたがオフフックではないと信じています。標準は、コードが意図したとおりに動作することを保証しませんが、多くの人がさまざまな「自己登録」構造の動作に依存しています。

オブジェクトtは動的に初期化されます。これには、呼び出しの副作用がありfます。標準では、静的に格納されたオブジェクトの動的な初期化について次のように述べています (3.6.2/4、「非ローカル変数の初期化」)。

メインの最初のステートメントの前に、静的記憶域期間を持つ非ローカル変数の動的初期化が行われるかどうかは、実装によって定義されます。初期化が main の最初のステートメントの後のある時点まで延期される場合、初期化される変数と同じ変換単位で定義された関数または変数の最初の odr-use (3.2) の前に発生するものとします。

あなたのコードでは、 xodr のみが使用されていxますが、メインの翻訳単位で定義されています。他の TU の変数や関数はプログラムで ODR 使用されないため、技術的にはt初期化される保証はありません。技術的には、すべてを初期化するには、プログラムの静的制御フローによってすべての TU から何かを参照する必要があります。

私が言ったように、「自己登録」変換単位 (たとえば、文字列キー付きマップにファクトリ関数ポインターを登録する) を備えた実際のコードがたくさんあるため、TU を最終的なプログラムに追加するだけで終了します。より多くの機能を搭載。ほとんどのコンパイラは、すべてのグローバル変数を無条件に初期化すると言われています。そうしないと、多くの実際のコードが壊れてしまうからです。しかし、それに依存しないでください!

于 2012-12-10T00:34:14.890 に答える
3

標準では、翻訳単位を選択してプログラム全体に結合する方法については実際には述べられていません。私の知る限り、これについて C++98 と C++11 の間で重要な変更はありません。

実際には、TU を としてリンクすると.o、何があってもその静的初期化子を取得しますが、 の一部としてリンクすると.a、TU 内の他の何かが推移的に参照された場合にのみ静的初期化子を取得します。から、main()または としてリンクされた別のファイル.old--whole-archiveフラグはこれをオーバーライドし、アーカイブの各メンバーを個別にリストしたかのように取り込みます.o。他のリンカーは、これを別の方法で処理する場合があります。

于 2012-12-10T00:36:03.967 に答える
2

関連するセクションは、2.2 [lex.phases] パラグラフ 1、項目 8 および 9 です。

.8. 翻訳された翻訳単位とインスタンス化単位は次のように結合されます: [ 注: これらの一部またはすべてがライブラリから提供される場合があります。—終わりの注] 翻訳された各翻訳単位を調べて、必要なインスタンス化のリストを作成します。[ 注: これには、明示的に要求されたインスタンス化が含まれる場合があります (14.7.2)。—終わりの注] 必要なテンプレートの定義が配置されています。これらの定義を含む翻訳単位のソースが利用可能である必要があるかどうかは、実装によって定義されます。[ 注: 実装は、ソースがここで必要とされないようにするために、翻訳された翻訳単位に十分な情報をエンコードできます。—終わりの注] インスタンス化ユニットを生成するために必要なすべてのインスタンス化が実行されます。[ 注: これらは、翻訳された翻訳単位に似ています。ただし、インスタンス化されていないテンプレートへの参照やテンプレート定義は含まれていません。—終わりの注] インスタンス化が失敗した場合、プログラムは不正な形式です。

.9. すべての外部エンティティ参照が解決されます。ライブラリ コンポーネントは、現在の翻訳で定義されていないエンティティへの外部参照を満たすためにリンクされています。このようなトランスレータの出力はすべて、実行環境での実行に必要な情報を含むプログラム イメージに収集されます。

項目 8 は、すべての翻訳単位を含める必要があることを示す最高のものです。項目 9 は、シンボルを解決するために必要なすべてのものも取り込まれると述べているだけです。事実上、これは明示的に翻訳単位を含めることで望ましい効果が得られることを意味します。ただし、翻訳単位をライブラリに入れることはできません。これはあなたが過去に経験したことだと思います: たとえば、実装をライブラリに入れ、起動時にそれらが登録されることを期待します。対応する翻訳単位のシンボルは参照されていないシンボルを解決しないため、ライブラリからのオブジェクト ファイルは取り込まれず、それに対応してグローバル オブジェクトは初期化されません。

于 2012-12-10T00:31:08.287 に答える