7

注:私はすでにここを見てきましたが、答えが正しいとは思いません。

関数のアドレスを取得するときに、関数の暗黙的なインスタンス化を管理する規則は何ですか? n3242 の 14.7.1/9 には次のように書かれています。

実装は、関数テンプレート、メンバー テンプレート、非仮想メンバー関数、メンバー クラス、またはインスタンス化を必要としないクラス テンプレートの静的データ メンバーを暗黙的にインスタンス化してはなりません。

現在、そのアドレスを取得するために関数定義を持っている必要はありません。前方宣言された関数のアドレスを取得して、別の翻訳単位で定義することができます。

その場合、いつ必要になるかわかりません。それでも、コンパイラには独自の考えがあるようです。GCC と VC でのテスト。いくつかの例を次に示します。

template <typename T> void CallBanana() { T::Banana(); }
template <typename T> void CallUnimpl();

template <typename T>
struct S {
    static void CallBanana() { T::Banana(); }
    static void CallOrange() { T::Orange(); }
    static void CallUnimpl();
};

struct B { static void Banana() {} };

int main() {
    (void)(&CallBanana<void>); // 1
    (void)(&CallUnimpl<void>); // 2
    (void)(&S<void>::CallBanana); // 3
    (void)(&S<void>::CallOrange); // 4
    (void)(&S<void>::CallUnimpl); // 5
    (void)(&S<B>::CallBanana); // 6
}

効果を確認するには、これらを一度に 1 つずつコメントインする必要があります。

ここでテストされたGCC 4.7は、1、3、および 4 について不平を言います。したがって、存在する場合はすべての定義をインスタンス化しています。

VC 2010 (オンライン テストなし、申し訳ありません) は 3 と 4 をインスタンス化しますが、1 はインスタンス化しません。

ここでテストしたClang 3.0は、VC 2010 と同じ動作をします。

2 または 5 について不平を言うコンパイラはありません。ただし、実際にこれらのポインターを使用した場合、リンクに失敗すると予想されます。

すべてのコンパイラで、6 回コンパイルされます。私はこれを期待していますが、単一の関数のアドレスを取得したという理由だけで、クラステンプレート全体がインスタンス化されていないことを示すことを目的としています (他の質問への回答で主張されているように)。テンプレート全体がインスタンス化されている場合、B には Orange 関数が含まれていないため、S::CallOrange はコンパイルされません。

したがって、正しい動作がどうあるべきかについて、誰かが決定的な答えを持っているかどうか疑問に思っています。標準では、関数をインスタンス化するべきではないと主張しているようですが、3 つの一般的なコンパイラが場合によってはインスタンス化しますが、それぞれにも違いがあります。

4

2 に答える 2

6

(評価されたコンテキスト内で) アドレスを取得する場合に必要な関数の定義。

もちろん、定義は別の翻訳単位で指定できますが、定義が必要であるという事実は変わりません。

必要なメンバー関数が 1 つだけの場合でも、他のメンバー関数もインスタンス化されるとは限りません。

関数テンプレートが未定義の場合、暗黙的にインスタンス化することはできません。次に、別の翻訳単位で明示的にインスタンス化する必要があります。別の翻訳単位の暗黙的なインスタンス化に依存することは許可されていません (ただし、診断は必要ありません)。

于 2013-04-24T16:28:03.167 に答える