2

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

struct X {
  static const int i = 45;
};

void foo() {
  const int* k = &X::i;
}

int main() {
}

お気に入りのコンパイラを開かずに、この単純な獣をコンパイルしてリンクしようとすると、どのような結果になると思いますか?

コンパイラだけでなく、その最適化オプションにも依存することに驚く人もいるかもしれません! たとえば、gcc では、コードは最適化をオフにするとリンクを拒否しますが、最適化をオンにすると喜んでリンクします (そして runnable-doing-nothing 実行可能ファイルを生成します)。

失敗した場合の診断はおかしいでしょう - シンボルX::iが見つからないでしょう。破棄されるため、最適化が有効なリンクは成功X::iします。

そして質問。このコードをコンパイルするコンパイラの動作は正しいですか? X::iにはリンケージがないので、このシンボルでリンケージを要求するコードを生成するように要求されたときに、コンパイラーは文句を言うべきではありませんか?

4

1 に答える 1

3

アドレスを取得してODR を使用 iしているため、クラスの外で定義する必要があります。

const int X::i ;

この規則の違反は、診断不要のカテゴリに分類されるため、これは完全に有効な動作です。

非形式的には、オブジェクトは、そのアドレスが取得されるか参照がバインドされている場合に ODR 使用され、関数は、関数呼び出しが行われるかアドレスが取得される場合に ODR 使用されます。オブジェクトまたは関数が ODR で使用される場合、その定義はプログラムのどこかに存在する必要があります。これに違反すると、リンク時エラーになります。

これは、コンパイラ、最適化レベルなどに依存する可能性があります...

ドラフト C++ 標準セクション3.2[basic.def.odr] は次のように述べています。

すべてのプログラムには、そのプログラムで ODR で使用されるすべての非インライン関数または変数の定義が 1 つだけ含まれている必要があります。診断不要

いくつかのメモ:

  • 一般に、odr 違反を検出するのは難しい問題です。
  • iこの場合、後でまたは別の翻訳単位で定義される可能性があるため、それをキャッチするのは見た目ほど単純ではありません。
  • 定義は 1 つしか必要ないため、行外の定義が必要ですが、宣言は繰り返すことができます (つまり、ヘッダー ファイルの一部である場合)。
于 2015-11-19T19:38:16.190 に答える