2012-06-26 20 views
9

最小限のコード:インライン関数の定義が異なると、定義されていない動作ですか?

// --------inline.h-------- 
struct X { 
    static inline void foo(); 
}; 
#ifdef YES 
inline void X::foo() { cout << "YES\n"; } 
#else 
inline void X::foo() { cout << "NO\n"; } 
#endif 

// --------file1.cpp-------- 
#define YES // <---- 
#include"inline.h" 
void fun1() 
{ 
    X::foo(); 
} 

// --------file2.cpp-------- 
#include"inline.h" 
void fun2() 
{ 
    X::foo(); 
} 

我々はfun1()fun2()を呼び出した場合、その後、彼らは同じX::foo()の異なる機能体を参照している意味し、それぞれYESNOを印刷します。

このコードにかかわらず、私の質問は
これは明確に定義されているか未定義の動作ですか?

+0

名前が異なる2つの異なるファイルに2つの異なるフリー・ファンクション(クラス・メンバーではない)を使用したい場合は、 'static'キーワード(または匿名名前空間)を使用してそれらを分離できます。 file2.cppの関数をstaticとマークすると、リンク中にほかの.cppファイルが認識できなくなります。これは非常に大きなプログラムの場合に便利です。ここでは、一般的な名前がすでに関数によってどのように使われているかを必ずしも確かめることができません。 – Crashworks

+0

この場合、inline.hと#defineハックは本当に必要ありません。 file1.cppでは "void X :: foo()"(インラインでもインラインでも)を片方だけ定義し、file2.cppではもう一方を定義すれば、まったく同じ動作になります。 – abarnert

+0

@abarnert: 'inline'を省略すると、1つの定義ルール違反になります。非インライン関数は3.2段落3、インライン関数は3.2段落5でカバーされます。多くの(ほとんどの)リンカーは非インライン違反をキャッチします。少なくとも私はインライン違反をキャッチしていません。 –

答えて

13

はいこれは未定義の動作です。

リファレンス:

C++ 03標準:

7.1.2関数指定子[dcl.fct.spec]
パラ4:

インライン関数は、それが使用されるすべての翻訳単位で定義され、正確にすべての場合において同じ定義(3.2)。 [注:インライン関数の呼び出しは、その定義が翻訳単位に現れる前に発生する可能性があります。 ]外部リンケージを持つ関数が1つの翻訳単位でインラインで宣言されている場合は、それが現れるすべての翻訳単位でインラインで宣言されます。診断は必要ありません。外部リンクを持つ インライン関数は、すべての翻訳単位で同じアドレスを持つものとします。 externインライン関数の静的ローカル変数は、常に同じオブジェクトを参照します。 externインライン関数の文字列リテラルは、異なる翻訳単位で同じオブジェクトです。

注:3.2は述べて一つの定義ルールのことをいう:

[basic.def.odr]
パラ1 3.2 1つの定義ルール:

ありません翻訳単位は、変数、関数、クラス型、列挙型またはテンプレートの複数の定義を含むものとする。

+0

+1。また、インラインはちょっとしたヒントに過ぎないことに注意してください(コンパイラやリンカがこの場合あなたに警告するよい機会を逃してしまうかもしれません)。 – abarnert

+3

@abarnert: 'inline'はヒント以上のものです。関数呼び出しが関数本体に置き換えられたのは、コンパイラの裁量ではありますが、' inline'を使用すると、コンパイラ**は特定の規則に従わなければなりません。ルール。 –

+0

はい、ルールが効果的に言っているのは、インラインを使用するとODRを回避できないということです。ただし、まったく同じ定義を繰り返すことは可能です。 – abarnert

7

未定義。あなたはODRに違反しています。

4

我々はFUN1()を呼び出し、fun2()した場合、その後、彼らは同じXの異なる機能体を参照している意味し、NOそれぞれYES印刷していないとします:: FOO()。

試しましたか?さまざまな最適化レベルですか?

最適化レベルと、コンパイルされたオブジェクトがリンカに提示される順序によって、YES、NO、YES、YES、NOとNOが表示されます。

言うまでもなく、これは未定義の動作です。

+0

+1この情報のために。私はそれに気づかない。私は '-O4'でコンパイルしました。 – iammilind

+1

@Als:大きく変化する動作を観察することは、未定義の挙動を示す(確かではないにしても)良い証拠です。それは危険です(一貫した行動が未定義の行動ではないという良い証拠ではありません)。 – abarnert

+3

@Als:私は観察された行動が証明ではないことを実感します。私はこれがあらかじめUBだったことは分かっていました。私は、何が生産されたのかを見て、別の結果を生み出すためにそれを持ち歩く方法を見ていくことは興味深いと思った。 –

関連する問題