2016-11-17 4 views
7

私は厄介な "1つの定義ルール"違反で噛まれました。 私のプロジェクトには微妙なバグがたくさんあることを恐れています。1つの定義ルールの警告

たとえば、次のプログラムは、Visual Studio 2015で、ヌルポインタ参照になります:

Source1.cpp: 
---------- 
struct S { 
    double d = 0; 
}; 

void Foo() { 
    S s; 
} 


Source2.cpp: 
----------- 
struct S { 
    int a = 0; 
}; 

int main() { 

    int value = 5; 
    int& valueRef = value; 
    S s;   // valueRef is erased due to S::d initialization from Source1.cpp 

    valueRef++; // crash 
} 

これは警告なしでコンパイルされます。

Source2.cppSource1.cppから何も使用していないので厄介です。プロジェクトからSource1.cppを削除してもまだコンパイルされており、もう問題はありません。

大きなプロジェクトでは、cppファイルが構造体またはクラスを定義済みの名前で「ローカル」に定義しないようにすることは非常に難しいようです。

は、私はいくつかのクラスを持っているようなPointSerieStateItem、...これは小さなCPPファイルでOKでしたが、私が、私はそれが安全ではありません実現します。

このようなバグを検出するコンパイラ警告はありますか? ODR違反を回避するベストプラクティスは何ですか?

+4

匿名名前空間で定義します。 – molbdnilo

答えて

3

この特定のケースでは、一番下のODR違反(実際に問題が発生しています)は暗黙的に定義されたのインラインコンストラクタSです。あなたのプログラムには、インラインS::S()の2つの一致しないバージョンの関数があります。これは、元のODR違反(異なるクラス定義の同じクラス)によって引き起こされた別のODR違反として見ることができます。

C++コンパイルインフラストラクチャに対する現在のアプローチでは、実装がこのエラーを「認識」することは困難です。もちろん、十分な努力をすることは可能です。

この場合、エラーを「可視」にするために、クラスコンストラクタを、空の本体を持つ非インライン関数として明示的に宣言して定義することができます。 2つの非インラインの存在S::S()はリンカエラーを引き起こします。

これは過度の人為的尺度として理解されるかもしれませんが、場合によってはクラスの「集約」ステータスが変更される可能性があります。

4

ODR違反を避けるベストプラクティスは何ですか?

これは基本的に名前空間を持つ理由です。

使用するソフトウェアコンポーネント(例えばbooststdasiosqlmytoolyourlibなど)ごとに1つの周知の名前空間。

名の名前空間は、実際にはその名前の一部を形成するので、以下の三つの別個のクラスで

namespace X { 
    struct S {}; 
} 

namespace Y { 
    struct S {}; 
} 

struct S {}; 

結果が定義されています。 1つはX::Sと呼ばれ、1つはY::Sと呼ばれ、もう1つはSであり、::Sとも呼ばれます。

::は、グローバル名前空間です。ここで宣言するのを避けることは良い考えです。これは、プログラムで使用するCコンポーネント(または純粋に記述されたC++コンポーネント)が、この名前空間を自分の名前ですばやく汚染するためです。

3
  • 強力なnamespaceで、でもコードの何百万人で、クラス/構造体の名前を整理することは難しいことではありません。だから、私はプログラマがODRに違反した場合、標準は明示的な診断を必要としないことを覚えておいてくださいnamespace

  • 匿名を試してみてください、あなたが実際にいくつかの定義をしたい場合namespaceは「ローカル」、ネストされたレベルに

  • を定義することができることを忘れないでください。自分自身に数える。

関連する問題