2015-09-18 1 views
5

私は静的変数でテンプレートを宣言し、また、それを定義するヘッダファイルを持っている:このODR違反で噛まれないことを保証しますか?

/* my_header.hpp */ 
#ifndef MY_HEADER_HPP_ 
#define MY_HEADER_HPP_ 

#include <cstdio> 

template<int n> 
struct foo { 
    static int bar; 

    static void dump() { printf("%d\n", bar); } 
}; 

template<int n> 
int foo<n>::bar; 

#endif // MY_HEADER_HPP_ 

このヘッダはmain.cppおよび共有ライブラリmylibの両方が含まれます。特に、mylib_baz.hppにはこのテンプレートだけが含まれており、テンプレートの特殊化を変更する関数が宣言されています。

/* mylib_baz.hpp */ 
#ifndef MYLIB_BAZ_HPP_ 
#define MYLIB_BAZ_HPP_ 

#include "my_header.hpp" 

typedef foo<123> mylib_foo; 
void init_mylib_foo(); 

#endif // MYLIB_BAZ_HPP_ 

/* mylib_baz.cpp */ 
#include "mylib_baz.hpp" 
void init_mylib_foo() { 
    mylib_foo::bar = 123; 
    mylib_foo::dump(); 
}; 

Iは(mylib_baz.oを含む)mylib.soを行うと、foo<123>::barのシンボルが存在し、弱い宣言。しかし、foo<123>::barのシンボルは、私のmain.oにも弱い宣言されています。

/* main.cpp */ 
#include "my_header.hpp" 
#include "mylib_baz.hpp" 

int main() { 
    foo<123>::bar = 456; 
    foo<123>::dump(); /* outputs 456 */ 
    init_mylib_foo(); /* outputs 123 */ 
    foo<123>::dump(); /* outputs 123 -- is this guaranteed? */ 
} 

私が一つの定義ルール(両方my_header.cppmain.cppで定義されたfoo<123>::bar)に違反していますことが表示されます。しかし、g ++とclangの両方でシンボルが弱(またはユニーク)であると宣言されているので、これで噛まれることはありません - すべてのアクセスはfoo<123>::barに変更されます。

質問1:これは偶然の一致です(多分ODRは、テンプレートの静的メンバのための動作の相違点)、または私は実際には標準でこの動作を保証するのですか?

質問2:私は観察している動作をどのように予測できましたか?つまり、正確にコンパイラがシンボルを弱く宣言するのは何ですか?

+1

私は標準では「動作してはいけません」と言っていますが、あなたのリンカーは「それはOKです」と言います。 ODRに違反することは悪い考えです。コードはどこでも動作しません。 Cでは、複数の定義を動作させる '共通の拡張'があります([C言語のソースファイル間で変数を共有するにはどうすれば 'extern'を使用しますか?](http://stackoverflow.com/questions/1433204/how-do-i-use-extern-to-share-variables-between-source-files-in-c/) - この機能は実際にはCとC++のコンパイラがリンカ技術を共有する機会があります) –

+0

合意しました!私はこれもうまくいかないとはかなり確信していますが、私は本当になぜそれに私の指を置くことはできません。私の場合、私は '' bar''externを宣言し、 'mylib_baz.cpp'で定義しただけです(そして、そうするべきです)。しかし、私はもっと深く掘り下げて、私が観察している振る舞いの背後にある論理的根拠を見るのは本当に興味があります。 – FreenodeForsakeMe

+0

"foo <123> :: barはmy_header.cppとmain.cppの両方で定義されています" ...何?私はそれがあなたが言及したものではない一つの場所で定義されているのを見ています: 'my_header.hpp'。それは一つの定義です。 – Barry

答えて

1

ODR違反はありません。 の1つがの定義barです。それはここにある:barとして

template<int n> 
int foo<n>::bar; // <== 

は、すべての翻訳単位間で1つの定義があることを示している、staticです。 barはすべてのオブジェクトファイルに一度表示されますが(結局シンボルが必要です)、リンカはそれらを一緒にマージしてbarの真の定義にします。

$ g++ -std=c++11 -c mylib_baz.cpp -o mylib_baz.o 
$ g++ -std=c++11 -c main.cpp -o main.o 
$ g++ main.o mylib_baz.o -o a.out 

が生成されます:あなたがいることがわかります

$ nm mylib_baz.o | c++filt | grep bar 
0000000000000000 u foo<123>::bar 
$ nm main.o | c++filt | grep bar 
0000000000000000 u foo<123>::bar 
$ nm a.out | c++filt | grep bar 
0000000000601038 u foo<123>::bar 

uが示す場合:

"U"
シンボルは一意のグローバルシンボルです。これは標準的なELFシンボル束縛のGNU拡張です。このようなシンボルの場合、動的リンカーは、プロセス全体でこの名前とタイプのシンボルが1つだけ使用されていることを確認します。

+0

'static'はこれに責任がありません。それは 'bar'がテンプレートのメンバーであり、そのテンプレートと一緒にインスタンス化される(そして翻訳単位間で統一される)ことができます。 –

関連する問題