8

ODR に関する質問はたくさんありますが、探しているものが見つからないので、これが重複していたり​​、タイトルが不適切だったりしたら申し訳ありません。

次の点を考慮してください。

struct t {t(*id)();};

template<typename T>
t type() {return {type<T>};}

これは、 type ごとに一意の識別子を定義しようとする私の試みを単純化しすぎたものであり、異なるコンパイル ユニット間で一意のままであることを願っています。

T特に、 のような具象型が与えられ、std::string2 つの異なるコンパイル単位がヘッダー ファイルに上記のコードを含むと仮定すると、次の式が必要です。

type<T>().id

両方の単位で( type の) 同じ値を取るため、t(*)()type の一意の識別子として機能しTます。

値は関数のアドレスなtype<T>ので、問題はプログラム内type<T>で一意の関数が一定義規則で保証されているかどうかです。iso 3.2/3 は言う

すべてのプログラムには、そのプログラムで ODR で使用されるすべての非インライン関数または変数の定義が 1 つだけ含まれている必要があります。

どこで 3.2/2

名前が潜在的に評価される式または [...] として表示されるオーバーロードされていない関数は、[...] でない限り、odr で使用されます。

アドレスが取得された場合、関数は非インラインであると想定します(ただし、標準では見つかりません)。

iso 3.2/5 には多くの例外がリストされていますが、関数への唯一の参照は

外部リンケージを持つインライン関数、[...]、非静的関数テンプレート、[...]、クラス テンプレートのメンバー関数、または一部のテンプレート パラメーターが指定されていないテンプレートの特殊化 [...]

ここではそうではないようです。

検証可能な例では、複数のファイルが必要になります。実際、 Dieter Lückingによって失敗したと主張されている例が示されていますが、私の場合は失敗していません (これを「保証」とは見なしません)。

それで、これはうまくいくでしょうか?

4

1 に答える 1

4

したがって、3.2/5 は実際にはかなり強力なサポートのようです。まず、定義はオブジェクト コードの構成要素ではなく、ソース コードの構成要素であることに注意してください。ただし、明らかに非常に密接な関係があります。3.2/5 は、非静的関数テンプレートの定義が複数あっても問題なく、さらにそのような場合、定義が 1 つしかないかのように動作する必要があると言っています。関数が異なる翻訳単位で異なるアドレスを持っていた場合、少なくとも私の読書では、定義が1つしかないかのように動作しません。

関数ポインタは非型テンプレート引数として渡すことができるため、これは特に当てはまります。このような引数は一定である必要があり、すべての翻訳単位で同じでなければなりません。たとえば、文字列リテラルは、そのアドレスが翻訳単位によって異なるため、正確にはそのような引数にはなりません。

すべての要件が満たされているかどうかは、名前解決などを扱うため、複数の定義のコンテキストに正確に依存します。ただし、それらはすべて「ありふれた」要件であり、 「もちろん」タイプ。たとえば、違反は次のようなものです。

file1.cpp

static int i;

// This is your template.
template <typename T>
void foo() {
    i; // Matches the above i.
}

file2.cpp

static int i;

// This is your template. You are normally allowed to have multiple
// identical definitions of it.
template <typename T>
void foo() {
    // Oops, matches a different entity. You didn't satisfy the requirements.
    // All bets are off.
    i;    
}

Linux では、ウィーク シンボルを介して複数の定義がサポートされていることを知っています。実際、Linux では、まさにこれが原因で Lucking の例は失敗しません。プラットフォームを尋ねる彼の答えにコメントを残しました。リンク時に、リンカーは弱いシンボルのインスタンスを 1 つを除いてすべて破棄します。明らかに、インスタンスが実際に同じでない場合、それは悪いことです。しかし、3.2/5 のこれらの要件は、インスタンスが実際にはすべて同じであることを保証するように設計されているため、リンカーは 1 つしか保持できません。

補遺: 現在、Dieter Lucking は、コンパイルに問題があったと言っていますが、実際にはコンパイルに失敗していません。ただし、Visual Studio がこれをどのように処理するかについて、Windows DLL の内部に精通している誰かがここでコメントできるとよいでしょう。

于 2014-05-03T12:59:26.123 に答える