1. ホーム
  2. c++

[解決済み] extern テンプレートの使用 (C++11)

2022-07-07 16:40:53

質問

図1: 関数テンプレート

TemplHeader.h

template<typename T>
void f();

TemplCpp.cpp

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

メイン.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>;

メイン.cpp

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

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

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

どのように解決するのですか?

のみを使用する必要があります。 extern template を強制することです。 ではなく のとき、テンプレートのインスタンス化を強制します。 あなたが知っている でインスタンス化します。 これは、コンパイル時間やオブジェクトファイルのサイズを減らすために使用されます。

例えば

// 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> のようなテンプレートを何度も使う場合は extern を使うべきです。

また、クラスと関数が一体となっている場合や、テンプレートのメンバ関数にも適用されます。