2015-12-07 5 views
18

次のC++テストコードはリンクしていません(gcc 4.9.2、binutils 2.25)。エラーはIn function 'main': undefined reference to 'X::test'です。そのため09行の「名前空間を使用する」とは何を正確にしますか?

01: #include <string> 
02: #include <iostream> 
03: 
04: namespace X 
05: { 
06:  extern std::string test; 
07: }; 
08: 
09: using namespace X; 
10: std::string test = "Test"; 
11: 
12: int main() 
13: { 
14: std::cout << X::test << std::endl; 
15: } 

、私が代わりに無関係なtest変数がグローバル名前空間で宣言され、定義されていることを信じているライン06で宣言X::test変数、したがって、リンクエラーを定義するためにライン10を期待していました。

質問:私の期待が間違っていた理由と正確に何が起こっているのか説明してください。

ない答え:

  • 私はそれがstd::string X::test = "Test";にライン10をリンク変更することができます。
  • まず、 "using namespace"を使用しないでください。
+0

「extern」という名前空間の中で、名前空間を見つけることができます。名前空間の中で定義されることはなく、コンパイラはそれを見つけることができません。 'Undefined reference'。あなたに質問する:なぜそれは外出する必要があるのですか? – wouter140

+3

@wouter140: 'extern'は「名前空間外のものを探しています」とは関係ありません。単に「これは他の場所(extern)alに定義されています」という意味です。 – DevSolar

+1

@curiousguy言い換えれば、 "どこかの他の場所"、または実際はどこにいても。単に「これは単なる宣言であり、定義ではない」という意味です。 – Angew

答えて

26

ディレクティブusing namespace X;は、ディレクティブを含む名前空間内で名前空間Xの名前を表示します。つまり、その範囲内でnという名前を検索すると、X::nが見つかります。ただし、コンパイラには、それを探すためにが必要な場合のみ検索されます。あなたの例では

、この宣言:-あるとしてグローバル名前空間内の

std::string test = "Test"; 

は完璧な理にかなっています。他の宣言と同様に、名前testが導入されました。どこにでもそれを見る必要はありません。

これは魚の全く異なるやかんのようになります。このコードで

namespace X 
{ 
    struct C 
    { 
    static std::string test; 
    }; 
} 

using namespace X; 
std::string C::test = "Test"; 

C::testの定義の意味を理解することが何であるかを知っているCを必要コンパイラ。したがって、の名前検索を実行します。usingディレクティブのおかげで、実際にはX::Cが見つかります。

+1

答えのスポット。構造体のシナリオも非常に面白いです。 – marcv81

7

using namespaceはあなたが指定された名前空間から定義を使用意味し、それはあなたが定義するすべてはあなたが使用した名前空間で定義されていることを意味するものではありません。

この動作のロジックはかなりシンプルです。我々は、次の例があるとしましょう:

namespace X 
{ 
    extern string test; 
}; 

namespace Y 
{ 
    extern string test; 
}; 

using namespace X; 
using namespace Y; 

string test = "value"; 

あなたの例のロジックに続いて、コンパイラはちょうどあなたが名前空間を明示的を宣言しなければならないので、それは、testを定義する必要があります名前空間れるか分からないでしょう。実生活では、グローバル名前空間で定義されています。

test変数は、Xという名前空間の外で、externと宣言されています。リンカーはX::testの定義を探しますが見つからず、結果としてこのエラーが表示されます。

+0

@DevSolar、建設的な批評に感謝します。 – Alex

+1

私はあなたのコードにあいまいさがあることに同意します。しかし、私の例では、コンパイラがグローバルな名前空間に新しい変数を宣言して定義することを、私が使用している名前空間の既存の変数を見つけるのではなく、なぜ決定するのかを説明することに成功したとは思いません。 – marcv81

+0

@ marcv81おそらく、私の説明は最良のものではありませんが、この場合、グローバル名前空間に変数を入れたいのであれば、それを ':: test'として定義する必要があります。*明示的*宣言です。一般に、コンパイラはあなたが言うことを行い、あなたが望むものを推測するべきではありません。 – Alex

3

ここには、変数testがネームスペースXに宣言されています。

04: namespace X 
05: { 
06:  extern std::string test; 
07: }; 

これは変数の定義ではありません。変数は、値を取得する前に定義する必要があります。

変数を初期化すると、この宣言を定義することもできます。たとえば、

04: namespace X 
05: { 
06:  extern std::string test = "Test"; 
07: }; 

この場合、コードは正常にコンパイルされます。このステートメントで

14: std::cout << X::test << std::endl; 

修飾名X::testへのアクセスがあります。コンパイラは、変数に指定されている名前空間Xでこの名前を検索し、宣言を検出します。今は変数の値を取得する必要がありますが、その定義を見つけることができません。

このステートメントで

10: std::string test = "Test"; 

それが明示的に指定された名前空間の外で宣言されているため、グローバル名前空間で宣言され、定義された変数testがあります。

名前空間Xで宣言された変数を定義したい場合は

10: std::string X::test = "Test"; 
       ^^^^^^^ 

代わりの

10: std::string test = "Test"; 

を書くことができます。

usingディレクティブについては、ディレクティブが使用されている名前空間で、指定された名前空間で宣言された名前を導入します。非修飾名にtest

14: std::cout << test << std::endl; 
        ^^^^^ 

を使用して書くことが場合は、この名前が原因usingディレクティブに名前X::test::testを参照することができますので、その後、あいまいさがあるだろう例えば

関連する問題