web-dev-qa-db-ja.com

externテンプレートの使用(C ++ 11)

図1:関数テンプレート

TemplHeader.h

template<typename T>
void f();

TemplCpp.cpp

template<typename T>
void f(){
   //...
}    
//explicit instantation
template void f<T>();

Main.cpp

#include "TemplHeader.h"
extern template void f<T>(); //is this correct?
int main() {
    f<char>();
    return 0;
}

これはextern templateを使用する正しい方法ですか、または図2のようにクラステンプレートにのみこのキーワードを使用しますか?

図2:クラステンプレート

TemplHeader.h

template<typename T>
class foo {
    T f();
};

TemplCpp.cpp

template<typename T>
void foo<T>::f() {
    //...
}
//explicit instantation
template class foo<int>;

Main.cpp

#include "TemplHeader.h"
extern template class foo<int>();
int main() {
    foo<int> test;
    return 0;
}

このすべてを1つのヘッダーファイルに入れるのが良いことはわかっていますが、複数のファイルで同じパラメーターを使用してテンプレートをインスタンス化すると、同じ定義が複数得られ、コンパイラーはそれらをすべて(1つを除く)削除してエラーを回避します。 extern templateを使用するにはどうすればよいですか?クラスにのみ使用できますか、または関数にも使用できますか?

また、図1と図2は、テンプレートが単一のヘッダーファイルにあるソリューションに拡張できます。その場合、extern templateキーワードを使用して、複数の同じインスタンス化を回避する必要があります。これはクラスまたは関数に対してのみですか?

101
codekiddy

extern templateのみを使用して、コンパイラを強制的にnotにテンプレートをインスタンス化してくださいご存じが他の場所にインスタンス化される場合。コンパイル時間とオブジェクトファイルサイズを削減するために使用されます。

例えば:

// header.h

template<typename T>
void ReallyBigFunction()
{
    // Body
}

// source1.cpp

#include "header.h"
void something1()
{
    ReallyBigFunction<int>();
}

// source2.cpp

#include "header.h"
void something2()
{
    ReallyBigFunction<int>();
}

これにより、次のオブジェクトファイルが作成されます。

source1.o
    void something1()
    void ReallyBigFunction<int>()    // Compiled first time

source2.o
    void something2()
    void ReallyBigFunction<int>()    // Compiled second time

両方のファイルがリンクされている場合、1つのvoid ReallyBigFunction<int>()が破棄され、コンパイル時間とオブジェクトファイルのサイズが無駄になります。

コンパイル時間とオブジェクトファイルサイズを無駄にしないために、コンパイラーがテンプレート関数をコンパイルしないようにするexternキーワードがあります。これを使用する必要があります知っている場合のみそれは他のどこかの同じバイナリで使用されます。

source2.cppの変更:

// source2.cpp

#include "header.h"
extern template void ReallyBigFunction<int>();
void something2()
{
    ReallyBigFunction<int>();
}

次のオブジェクトファイルが作成されます。

source1.o
    void something1()
    void ReallyBigFunction<int>() // compiled just one time

source2.o
    void something2()
    // No ReallyBigFunction<int> here because of the extern

これらの両方が一緒にリンクされる場合、2番目のオブジェクトファイルは最初のオブジェクトファイルのシンボルを使用します。破棄する必要はなく、無駄なコンパイル時間とオブジェクトファイルサイズもありません。

これは、vector<int>などのテンプレートを複数回使用する場合など、1つのソースファイル以外のすべてでexternを使用する必要があるなど、プロジェクト内でのみ使用する必要があります。

これは、クラスと1つの関数、さらにはテンプレートメンバー関数にも適用されます。

160
Dani

ウィキペディアには 最良の説明 があります

C++ 03では、翻訳単位で完全に指定されたテンプレートが検出されるたびに、コンパイラはテンプレートをインスタンス化する必要があります。テンプレートが多くの翻訳単位で同じタイプでインスタンス化されている場合、これによりコンパイル時間が劇的に増加する可能性があります。 C++ 03でこれを防ぐ方法はないため、C++ 11ではexternデータ宣言に類似したexternテンプレート宣言が導入されました。

C++ 03には、コンパイラにテンプレートのインスタンス化を強制する次の構文があります。

  template class std::vector<MyClass>;

C++ 11は次の構文を提供するようになりました。

  extern template class std::vector<MyClass>;

これは、この翻訳単位でテンプレートをインスタンス化しないようコンパイラーに指示します。

警告:nonstandard extension used...

Microsoft VC++にはこの機能の非標準バージョンがありました数年前から(C++ 03で)。コンパイラーは、異なるコンパイラーでもコンパイルする必要があるコードの移植性の問題を防ぐために警告します。

リンク先ページのサンプルを見て、ほぼ同じように機能することを確認してください。もちろん、otherの非標準コンパイラ拡張機能を同時に使用する場合を除き、メッセージはMSVCの将来のバージョンでなくなると予想できます。

43
sehe

テンプレートの既知の問題は、コードの肥大化です。これは、クラステンプレートの特殊化を呼び出すすべてのモジュールでクラス定義を生成した結果です。これを防ぐには、C++ 0x以降、キーワードを使用できます  クラステンプレートの専門化の前

#include <MyClass> extern template class CMyClass<int>;

テンプレートクラスの明示的なインスタンス化は、単一の翻訳単位でのみ行われる必要があります。テンプレート定義(MyClass.cpp)を持つものが望ましい

template class CMyClass<int>;
template class CMyClass<float>;
4
damirlj

以前に関数にexternを使用したことがある場合、テンプレートにもまったく同じ哲学が適用されます。そうでない場合は、単純な関数のexternを使用すると役立ちます。また、ヘッダーファイルにexternを配置し、必要に応じてヘッダーを含めることもできます。

0
qqqqq