web-dev-qa-db-ja.com

GCCおよびMSコンパイラのテンプレートインスタンス化の詳細

GCCおよびMSコンパイラでのコンパイル時および/またはリンク時のテンプレートのインスタンス化の処理方法の比較または具体的な詳細を誰かが提供できますか?このプロセスは、静的ライブラリ、共有ライブラリ、および実行可能ファイルのコンテキストで異なりますか?私は このドキュメント GCCがそれをどのように処理するかについて見つけましたが、情報がまだ物事の現在の状態を参照しているかどうかはわかりません。ライブラリをコンパイルするときに、そこで提案されているフラグを使用する必要があります。 -fno-implicit-templates

私が知っていること(必ずしも正しいとは限りません)は次のとおりです。

  • テンプレートは実際に使用するとインスタンス化されます
  • テンプレートは、明示的なインスタンス化の結果としてインスタンス化されます
  • 重複インスタンス化は通常、重複インスタンス化を折りたたむか、リンク時間までインスタンス化を延期することによって処理されます
45
celavek

インスタンス化のポイント

テンプレートは実際に使用するとインスタンス化されます

正確ではありませんが、大まかに。インスタンス化の正確なポイントは少し微妙です。Vandevoordeの/ Josuttisのすばらしい本の インスタンス化のポイント という名前のセクションにあなたを委任します。

ただし、コンパイラは必ずしもPOIを正しく実装するとは限りません。 Bug c ++/41995:関数テンプレートのインスタンス化のポイントが正しくありません


部分的なインスタンス化

テンプレートは実際に使用するとインスタンス化されます

それは部分的に正しいです。これは関数テンプレートにも当てはまりますが、クラステンプレートの場合、使用されるメンバー関数のみがインスタンス化されます。以下は整形式のコードです。

_#include <iostream>

template <typename> struct Foo {
    void let_me_stay() {
        this->is->valid->code. get->off->my->lawn;
    }

    void fun() { std::cout << "fun()" << std::endl; } 
};


int main () {
    Foo<void> foo;
    foo.fun();
}
_

let_me_stay()は構文的にチェックされますが(構文は正しいです)、意味的にはチェックされません(つまり、解釈されません)。


二相ルックアップ

ただし、後で解釈されるのは依存コードのみです。明らかに、_Foo<>_内では、thisは_Foo<>_がインスタンス化される正確なテンプレートIDに依存しているため、インスタンス化時間までFoo<>::let_me_alone()のエラーチェックを延期しました。 。

ただし、特定のインスタンス化に依存するものを使用しない場合、コードは適切である必要があります。したがって、以下はnot整形式です:

_$ cat non-dependent.cc
template <typename> struct Foo {
    void I_wont_compile() { Mine->is->valid->code. get->off->my->lawn; }
};
int main () {} // note: no single instantiation
_

Mineは、コンパイラがインスタンスの依存関係を判断できるthisとは異なり、コンパイラにとって完全に未知のシンボルです。

ここで重要な点は、C++が two-phase-lookup のモデルを使用し、最初のフェーズで非依存コードのチェックを行い、フェーズ2で依存コードのセマンティックチェックを行うことです。 (およびインスタンス化時間)(これもしばしば誤解されている、または未知の概念です。多くのC++プログラマーは、インスタンス化までテンプレートがまったく解析されないと想定していますが、それはMicrosoft C++からの単なる神話です)。


クラステンプレートの完全なインスタンス化

Foo<>::let_me_stay()の定義は、依存しているthisポインターに関して、エラーチェックが後で延期されたために機能しました。あなたが利用したであろう時を除いて

明示的なインスタンス化

_cat > foo.cc
#include <iostream>

template <typename> struct Foo {
    void let_me_stay() { this->is->valid->code. get->off->my->lawn; }
    void fun() { std::cout << "fun()" << std::endl; } 
};

template struct Foo<void>;
int main () {
    Foo<void> foo;
    foo.fun();
}

g++ foo.cc
error: error: ‘struct Foo<void>’ has no member named ‘is’
_


さまざまな翻訳単位でのテンプレート定義

明示的にインスタンス化する場合、明示的にインスタンス化します。また、allシンボルをリンカーに表示します。これは、テンプレート定義が異なる変換単位に存在する可能性があることも意味します。

_$ cat A.cc
template <typename> struct Foo {
    void fun();  // Note: no definition
};
int main () {
    Foo<void>().fun();
}

$ cat B.cc
#include <iostream>
template <typename> struct Foo {
    void fun();

};
template <typename T>
void Foo<T>::fun() { 
    std::cout << "fun!" << std::endl;
}  // Note: definition with extern linkage

template struct Foo<void>; // explicit instantiation upon void

$ g++ A.cc B.cc
$ ./a.out
fun!
_

ただし、使用するすべてのテンプレート引数を明示的にインスタンス化する必要があります。そうしないと、

_$ cat A.cc
template <typename> struct Foo {
    void fun();  // Note: no definition
};
int main () {
    Foo<float>().fun();
}
$ g++ A.cc B.cc
undefined reference to `Foo<float>::fun()'
_

2フェーズルックアップに関する注意事項:コンパイラが実際に2フェーズルックアップを実装するかどうかは、標準では規定されていません。ただし、準拠するには、動作するかのように機能する必要があります(加算または乗算は、必ずしも加算または乗算のCPU命令を使用して実行する必要はありません。

57
Sebastian Mach