5

C++ 標準の 3.5/4 節によると、次のようになります。

名前のない名前空間、または名前のない名前空間内で直接的または間接的に宣言された名前空間には、内部リンケージがあります。

同時に、パラグラフ 7.3.1.1 には注記 96) があります。

名前のない名前空間内のエンティティは外部リンケージを持っている可能性がありますが、それらは翻訳単位に固有の名前によって効果的に修飾されているため、他の翻訳単位からは決して見ることができません。

名前のない名前空間内の名前の外部リンケージを明示的に作成する方法と、別の翻訳単位から名前のない名前空間内で定義された名前にアクセスする方法がないことが標準で保証されている場合、リンケージが実際に外部であることを確認する方法は?

名前のない名前空間内の名前に対して明示的な外部リンケージを行うことが役立つのはどのような場合ですか?

4

3 に答える 3

4

名前のない名前空間内の名前に対して明示的な外部リンケージを行うことが役立つのはどのような場合ですか?

外部リンケージの必要性は、C++03 テンプレートにとって重要でした。たとえば、テンプレート引数としての関数ポインタは、外部リンケージの関数へのポインタでなければなりませんでした。たとえば、次のコードは C++03 コンパイラではコンパイルできません。

template< void(*f)() >
void tfunc() { f(); }

#include <stdio.h>
static void g() { printf( "Hello!\n" ); }

int main()
{
    tfunc<g>();
}

C++11 コンパイラで問題なくコンパイルできます。

そのため、C++11 では、外部リンケージを持ちながら翻訳単位間で名前が競合しない匿名名前空間メカニズムは、技術的にクラスにのみ必要です。クラスには外部リンケージがあります。他の翻訳単位では発生しないことが保証されているクラス名を選択する必要はありません。

C++11 では、テンプレート パラメーターだけでなく、匿名名前空間内のものに外部リンケージがあるかどうかのルールも変更されました。C++03 では、匿名名前空間自体が匿名名前空間 (C++03 §3.5/4 最後のダッシュ + C++03 §7.3.1.1/1) 内にない限り、匿名名前空間は正式に外部リンケージを持っていました。C++11 では、無名名前空間に正式な内部リンケージがあります。

名前空間のリンクがないため、これはリンカには関係ありませんが、物事のリンクを記述するための正式なデバイスとして重要です。

C++11 §3.5/4:

名前のない名前空間、または名前のない名前空間内で直接的または間接的に宣言された名前空間には、内部リンケージがあります。他のすべての名前空間には外部リンケージがあります。上記の内部リンケージが与えられていない名前空間スコープを持つ名前は、それが
変数の名前である場合、囲んでいる名前空間と同じリンケージを持ちます。または
— 関数; または
— 名前付きクラス (条項 9)、またはクラスがリンケージ目的で名前をtypedef持つ宣言で定義された名前なしクラス (7.1.3)。typedefまたは
— 名前付き列挙 (7.2)、またはtypedef列挙がリンケージ目的で typedef 名を持つ宣言で定義された名前のない列挙 (7.1.3)。または
— リンケージを持つ列挙に属する列挙子。また
— テンプレート。


他の質問に進む前に、標準からのこの引用に注意する価値があります。

名前のない名前空間のエンティティは外部リンケージを持っている可能性がありますが、それらは翻訳単位に固有の名前によって効果的に修飾されているため、他の翻訳単位からは決して見ることができません。

宣言されている名前空間に関係なく、エンティティは他の翻訳単位から見えるため、明らかに間違っています。extern "C"

幸いなことに、メモは非規範的です。つまり、メモは言語を定義しません。


名前のない名前空間内の名前に対して明示的に外部リンクを作成する方法

const非変数または関数を として宣言するだけexternです。const非変数または関数を as として 宣言しextern "C"、リンケージを extern にすることができますが、同時に、リンクに関する限り名前空間を無関係にすることができます。C 言語には名前空間がありません。

namespace {
    extern "C" void foo() {}    // Extern linkage
}  // namespace <anon>

void bar() {}  // Also extern linkage, but visible to other TUs.

リンクが実際に外部であることを確認する方法

リンケージは、C++11 では §3.2 である「 ODR 」と略されることが多い One Definition Rule との競合の可能性などに影響します。

したがって、リンケージの動作を確認する 1 つの方法は、上記のソースから生成された 2 つのオブジェクト ファイルをリンクすることです。これは、同じソース コードを持つ 2 つの翻訳単位があるかのようにします。

C:\my\forums\so\088> g++ -c anon.cpp -o xo & g++ -c anon.cpp -o yo

C:\my\forums\so\088> g++ main.cpp xo yo
yo:anon.cpp:(.text+0x0): `foo' の複数定義
xo:anon.cpp:(.text+0x0): 最初にここで定義
yo:anon.cpp:(.text+0x7): 複数の「bar()」定義
xo:anon.cpp:(.text+0x7): 最初にここで定義
collect2.exe: エラー: ld が 1 つの終了ステータスを返しました

C:\my\forums\so\088> _

リンカは、 の複数の定義について不平を言いますfoo。これは、C 言語バインディングと同様に、リンカに関する限り、inlineグローバル名前空間の非外部リンケージ メンバとして 2 つの (競合する可能性がある) 定義があるように見えるためです。

于 2016-02-09T10:08:33.140 に答える
3

名前のない名前空間内の名前の外部リンケージを明示的に作成する方法

私が考えることができる唯一の方法は、そのリンケージ名が名前空間修飾を無視するように、C 言語リンケージを与えることです。

namespace {
  extern void f() { }      // has internal linkage despite 'extern'
  extern "C" void g() { }  // ignores linkage of namespace
}
void (*p)() = f;  // ensure 'f' won't be optimized away

(標準を厳密に読むと、内部リンケージが必要であることが示唆されてgいますが、コンパイラはそうではないようです。)

また、標準が別の翻訳単位から名前のない名前空間内で定義された名前にアクセスする方法がないことを保証している場合、リンケージが実際に外部であることを確認する方法は?

通常、ELF コンパイラは非グローバル シンボルとの内部リンケージを実装するため、コードをコンパイルしてオブジェクト ファイルを調べることができます。

$ g++ -c linkage.cc
$ nm linkage.o
0000000000000000 t _ZN12_GLOBAL__N_11fEv
0000000000000007 T g
0000000000000000 D p

名前のない名前空間のマングルされた名前はコンパイラによって異なる場合がありますが、デマングルすると次のように表示されます。

$ nm -C  linkage.o
0000000000000008 t (anonymous namespace)::f()
0000000000000000 T g
0000000000000000 D p

小文字は、ローカル可視性tがあることを示します。つまりf、他のオブジェクト ファイルからリンクすることはできません。大文字は外部リンケージがTあることを示します。g

ただし、ELF 可視性は C++ 標準の一部ではなく、一部のコンパイラは ELF プラットフォームでも可視性を使用せずにリンケージを実装しているため、これは標準によって保証されていません。たとえば、EDG コンパイラは同じコードに対してグローバル シンボルを生成します。

$ nm linkage.o
0000000000000008 T _ZN23_GLOBAL__N__7_link_cc_p1fEv
0000000000000004 C __EDGCPFE__4_9
0000000000000000 T g
0000000000000000 D p
$ nm -C linkage.o
0000000000000008 T (anonymous namespace)::f()
0000000000000004 C __EDGCPFE__4_9
0000000000000000 T g
0000000000000000 D p

したがって、使用extern "C"すると、名前のない名前空間に表示されている場合でも、外部リンケージに名前を付けることができますが、名前ない名前空間を使用しないため、他の翻訳単位からその名前を参照できるため、メモは正しくありません範囲。これは、名前のない名前空間のエンティティが自動的に内部リンケージを持っていなかったときの C++03 からの単なる残り物であり、メモを修正または削除する必要があることを示唆しています (実際、TC は、DR 1603によって既に削除されたと指摘しています)。 )。

名前のない名前空間内の名前に対して明示的な外部リンケージを行うことが役立つのはどのような場合ですか?

役に立つ場面が思いつきません。

于 2016-02-09T11:53:41.363 に答える
1

標準 [3.5/2] によると:

名前に外部リンケージがある場合、それが示すエンティティは、他の翻訳単位のスコープまたは同じ翻訳単位の他のスコープからの名前によって参照できます。

名前に内部リンケージがある場合、それが示すエンティティは、同じ翻訳単位内の他のスコープからの名前で参照できます。

したがって、基本的に、この何かが定義されている翻訳単位とは異なる翻訳単位で何かを参照できる場合は、外部リンケージがあり、そうでない場合は内部リンケージがあります。したがって、質問からのメモを考えると:

名前のない名前空間内のエンティティは外部リンケージを持っている可能性がありますが、それらは翻訳単位に固有の名前によって効果的に修飾されているため、他の翻訳単位からは決して見ることができません。

名前はあっても知らない、という状況が実際にはあるので、いくら頑張っても別の翻訳単位からは参照できない。そして、内部リンケージのあるものと区別がつかないようにします。ですから、私の意見では、これはジャグリングという言葉にすぎません。ある状況を別の状況と区別できない場合、それらは同じです。

于 2016-02-09T11:03:33.637 に答える