問題タブ [one-definition-rule]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c++ - std::make_unique、匿名名前空間および ODR
次のテストケースを検討してください (LLVM ソースから縮小):
と
これは 1 つの定義規則に違反していますか?
gcc-6 は現在、次のように考えています。
c++ - 複数のクラスでスタックを実装する場合の 1 つの定義規則 (ODR)
単純なスタック実装が機能していないようです。私は単純に、2 つの異なるクラス (クラス B とクラス C) を取得して、3 番目のクラス (クラス A) によって管理されている同じスタック内の要素をプッシュおよび印刷できるようにしようとしています。
A.cpp
ああ
B.cpp
C.cpp
私は 1 つの定義ルールを破っていると思います。私は正しいですか?
c++ - クラスメンバアクセス式に関する1つの定義規則
N4296、3.2 [basic.def.odr]p3:
x
名前が潜在的に評価される式として表示される変数は、左辺値から右辺値への変換を適用して、自明でない関数を呼び出さない定数式を生成しない限り、ex
によって ODR 使用されます。左辺値から右辺値への変換が に適用されるか、または破棄された値の式である、式 の潜在的な結果のセット。ex
x
x
ex
e
e
e
この段落をどう説明する?2つの説明が見つかりました。
ここから 1 「C++14 で [basic.def.odr]/2 を理解しようとしています (N4140)」
これをいくつかのステップに分けてみましょう: 式 `ex` 内の変数 `x` の出現は、次の場合を除き、odr-use を構成します:
ex
潜在的に評価されていないか、または- 次のすべてを満たす必要があります。
- 「左辺値から右辺値への変換を適用する
x
と、重要な関数を呼び出さない定数式が生成されます」 および- "
ex
は、式の潜在的な結果のセット " の要素でありe
、 次のいずれかが成り立ちます:
- "左辺値から右辺値への変換が適用される
e
"- 「または
e
破棄値式です」
および cppreference http://en.cppreference.com/w/cpp/language/definitionからの 2
次のいずれかが真でない限り
x
、潜在的に評価される式の変数ex
は ODR 使用されます。
左辺値から右辺値への変換を適用する
x
と、重要な関数を呼び出さない定数式が生成されます
x
はオブジェクトであり、 ex はより大きな式の潜在的な結果の 1 つですe
。ここで、そのより大きな式は、破棄された値の式または左辺値から右辺値への変換のいずれかです。
2 つのルールについての最初の答えはandで、もう 1 つはanyです。どちらが正しいですか?
このコードを説明するには、ルールをいくつかのステップに分けてください:
c++ - 以下は実際に ODR に違反していますか?
ここから:
この関数は、ODR ([basic.def.odr] §3.2/6) に 2 回違反します。これは、コンストラクター 2 の引数のどちらも左辺値から右辺値への変換を受け取らないためです。したがって、それらはアドレスで渡されますが、const (および constexpr) は内部リンケージを意味するため、アドレスは TU に依存します。
最初はそうだと思ったのですが、問題はmagic_number
内部リンケージです。内部リンケージがあるため、本質的にmagic_number
は、それらが異なる翻訳単位の異なる変数であるかのように扱われ、したがって同じ変数の複数の定義としてではありませんか? C++ 標準の最新のワーキング ドラフトの引用を使用して、誰かがこれを指定できますか?
c++ - インライン関数の ODR 違反を検出する方法はありますか?
だから私は2つの別々の翻訳単位でこのコードを持っています:
正常にコンパイルされると、結果は10
. -O3 (インライン展開) でコンパイルすると、11
.
に対して ODR 違反を行ったことは明らかですfunc()
。
異なるdllのソースをより少ないdllにマージし始めたときに現れました。
私が試してみました:
- GCC 5.1
-Wodr
(必要-flto
) - ゴールドリンカー
-detect-odr-violations
ASAN_OPTIONS=detect_odr_violation=1
アドレス サニタイザーを使用してインストルメント化されたバイナリを実行する前に設定します。
Asan は、他の ODR 違反をキャッチできると思われます (異なるタイプのグローバル変数またはそのようなもの...)
これは C++ の非常に厄介な問題であり、それを検出するための信頼できるツールがないことに驚いています。
おそらく、私が試したツールの 1 つを悪用したのでしょうか? または、これには別のツールがありますか?
編集:
func()
大幅に異なる 2 つの実装を作成しても、同じ量の命令にコンパイルされないため、問題に気付かないままです。
これは、クラス本体内で定義されたクラス メソッドにも影響します。それらは暗黙的にインライン化されます。
多くのコピー/貼り付けとその後の小さな変更を伴うレガシーコードは楽しいものです。
c++ - 1 つの定義規則に関する C++ の混乱
One Definition Ruleについて読んでいました。次のように述べています。
1 つの .cpp ファイルがstruct S { int x;を定義している場合。}; もう 1 つの .cpp ファイルはstruct S { int y;を定義します。}; 、それらをリンクするプログラムの動作は未定義です。これは通常、名前のない名前空間で解決されます。
未定義の理由と方法がわかりませんか?誰かがこの背後にある実際の理由を説明してくれますか? 名前のない名前空間でどのように解決されますか?
c++ - ODR で使用される空のクラスの最適化
今日の C++ コードの多くは、テンプレートを最大限にロードする傾向があります。それらはライブラリです: STL、Boost.Spirit、Boost.MPL など。それらは、フォームで機能オブジェクトを宣言することをユーザーに奨励します
struct S { /* presence of non-virtual member functions and operators, but absense of non-static data members or non-empty base classes */ }; S const s{};
。それらのほとんどはステートレスです (つまりstatic_assert(std::is_empty< S >{});
、保持します)。それらのうち、ODR で使用されるものについてはdata
、ファイルの空のセクションに関係なく、1 バイトずつ増加します (結果的に割り当てられたオブジェクトのすべてのアドレスが異なる必要があるためsizeof(S) == 1
、空のタイプの場合)。S
Boost.Spirit の単純な文法でも、このような ODR で使用される空のクラスはたくさんあります。しかし、それらのためにスペースを確保することはまったく意味がありません。
次のコードを使用してclang
、coliruでテストしようとしました ( -Ofast
):
結果を取得します ( size
utility forの出力DIM == 100
、つまり 100 * 100 クラス):
ODR の使用を抑制するために署名を変更diff(lhs & l, rhs & r)
すると、結果は次のようになります。diff(lhs l, rhs r)
data
行の単純なコメントの場合 (セクションは重要なだけです) とほぼ同じです (セクションassert((check< DIM >()));
の大部分text
は予測可能な DCE 最適化アウトです):
したがって、ODR で使用される空のクラスの最適化はないと結論付けます。
明示的に指定されたテンプレート パラメーターには、単純な typefilter を使用する可能性があります。
しかし、推測されたテンプレートの種類に対する簡単な回避策は思い浮かびません。
最新のコンパイラでは、上記の最適化が暗示されていますか? はいの場合、それを有効にする方法は?いいえの場合、現時点で望ましい動作を実現するための手法はありますか?
オブジェクトのアドレスがつぶやくことがあることは知っていますが、上記の状況ではそうではありません。
[[immaterial]]
変数や型の属性のようなもの(例)が便利だと思います。おそらく、そのような属性 (クラスに使用される) は、属性付きクラスのインスタンスのアドレスを取得する可能性を否定する必要があります (コンパイル時のハードエラー) か、アドレスの演算子は&
意味のない値を返す必要があります (実装定義)。
c++ - クラス static const ODR 内
メンバーstatic
のクラス内初期化に少し混乱しています。const
たとえば、以下のコードでは:
定義しませんFoo::n
(行はコメント化されています)。f(Foo::n)
したがって、リンク時に呼び出しが失敗することを期待していますが、実際にそうです。ただし、次の行std::cout << g(Foo::n) << std::endl;
は、.gcc などの最適化フラグを使用するたびに、gcc によってのみ正常にコンパイルおよびリンクされます (clang は依然としてリンカー エラーを出力します) -O1/2/3
。
- 最適化がオンになっている場合、gcc (gcc5.2.0 および gcc 4.9.3 で試行) がコードをコンパイルおよびリンクするのはなぜですか?
- そして、クラス内の static const メンバーの唯一の使用法は、呼び出しのようなテンプレート パラメーターなどの定数式であると言うのは正しい
h<Foo::n>
ですか?その場合、コードはリンクする必要がありますか?
c++ - 同じ名前の C++ インライン関数と外部関数が予期しない結果をもたらす
次のコードは、1 つの定義規則に違反していませんが、予期しない結果をもたらしています。
Test.hpp
Test1.cpp
Test2.cpp
main.cpp
「1 99」と出力されることを期待していますが、常に「1 1」と出力されます。
Test::test の 2 つの定義については、そのうちの 1 つがインライン定義であるため、One Definition Rule にも違反していません。
したがって、このプログラムは有効ですが、期待される結果が出力されません...
このプログラムに問題はありますか? それとも、ODR ルールについて誤解しているのでしょうか? (C++ 標準への参照が役立ちます)。
c++ - C++ での仮想関数のコンパイル時の静的型チェック
バックグラウンド
最近、私の同僚が、ライブラリの古いバージョンのヘッダー ファイルが使用されているという問題に遭遇しました。その結果、C++ で仮想関数を呼び出すために生成されたコードが、クラスの仮想関数ルックアップ テーブル ( vtable ) 内の間違ったオフセットを参照していました。
残念ながら、このエラーはコンパイル中にキャッチされませんでした。
質問
すべての通常の関数は、正しい関数 (正しいオーバーロード バリアントを含む) がリンカによって選択されるように、マングルされた名前を使用してリンクされます。同様に、オブジェクト ファイルまたはライブラリには、C++ クラスのvtable内の関数に関するシンボリック情報が含まれていると想像できます。
g++
リンク中に C++ コンパイラ (または Visual Studio など) が仮想関数の呼び出しを型チェックできるようにする方法はありますか?
例
簡単なテスト例を次に示します。この単純なヘッダー ファイルと関連する実装を想像してください。
ベース.hpp:
派生.cpp:
ここで、プログラムが間違った/古いバージョンのヘッダー ファイルを使用し、中央の仮想関数が欠落していると想像してください。
BaseWrong.hpp
ここにメインプログラムがあります:
メイン.cpp
正しいヘッダーを使用して「ライブラリ」をコンパイルし、次に間違ったヘッダーを使用してメイン プログラムをコンパイルおよびリンクすると…</p>
…その後、 への仮想呼び出しがvtableh()
で間違ったインデックスを使用するため、呼び出しは実際には:g()
この小さな例では、関数g()
と関数h()
は同じシグネチャを持っているため、「唯一の」問題は間違った関数が呼び出されていることです (これはそれ自体が悪いことであり、完全に気付かれない可能性があります)。たとえば、Pascal 呼び出し規則が使用されている Windows 上の DLL で関数を呼び出す場合 (呼び出し元が引数をプッシュし、呼び出し先が戻る前に引数をポップする)。