テンプレートのインスタンス化とは、関数テンプレートまたはクラス テンプレートから具象関数または具象クラスを作成することです。テンプレートのインスタンス化の作成は、暗黙的 (コンパイラー生成) または明示的 (ユーザー提供) にすることができます。
特定のテンプレート引数のテンプレートが必要な場合は、コンパイラがテンプレートを自動生成します。場合によっては、ヘッダー ファイルからテンプレート定義を削除したい場合や、計算能力を消費するテンプレートのインスタンス化を避けたい場合があります。この場合、明示的なインスタンス化が役に立ちます。
暗黙のインスタンス化
暗黙的なインスタンス化をデフォルトで選択する必要があります。暗黙的なインスタンス化とは、提供されたテンプレート引数の具体的な関数またはクラスをコンパイラが自動的に生成することを意味します。一般に、コンパイラは関数の引数からテンプレート引数も推測します。 C++17 では、コンパイラはクラス テンプレートのテンプレート引数を推測することもできます。
// implicitTemplateInstantiation.cpp #include <iostream> #include <string> #include <vector> template <typename T> class MyClass{ public: MyClass(T t) { } std::string getType() const { return typeid(T).name(); } }; template<typename T> bool isSmaller(T fir, T sec){ return fir < sec; } int main(){ std::cout << '\n'; std::cout << std::boolalpha; std::vector vec{1, 2, 3, 4, 5}; // (1) std::cout << "vec.size(): " << vec.size() << '\n'; MyClass myClass(5); // (2) std::cout << "myClass.getType(): " << myClass.getType() << '\n'; std::cout << '\n'; std::cout << "isSmaller(5, 10): " << isSmaller(5, 10) << '\n'; // (3) std::cout << "isSmaller<double>(5.5f, 6.5): " << isSmaller<double>(5.5f, 6.5) << '\n'; // (4) std::cout << '\n'; }
行 (1) と (2) は、クラス テンプレートの引数推定 (CTAG) を使用します。 std::vector
または MyClass
コンストラクターの引数からその型を推測できます。行 (3) は、そのテンプレート引数も推定します。逆に (4) 行では、テンプレート引数 double
明示的に指定されています:isSmaller<double>(5.5f, 6.5
).
コンパイラは、暗黙的なテンプレートのインスタンス化ごとに、具体的な関数またはクラスを作成します。 C++Insights はこのプロセスを視覚化します。
この自動プロセスは非常に快適ですが、いくつかの欠点があります。
<オール>どちらの問題も、テンプレートを明示的にインスタンス化することで解決できます。
明示的なインスタンス化
明示的なインスタンス化には 2 つの特徴があります。明示的なインスタンス化の定義と明示的なインスタンス化の宣言。
- 明示的なインスタンス化定義の構文:
template <template declaration>
- 明示的なインスタンス化宣言の構文:
extern template <template declaration>
構文を調べると、キーワード extern
明示的なテンプレートのインスタンス化とは、テンプレートの定義を生成することを意味します。以下は簡単な例です。
// explicitTemplateInstantiation.cpp #include <iostream> #include <string> #include <vector> template <typename T> class MyClass{ public: MyClass(T t) { } std::string getType() const { return typeid(T).name(); } }; template<typename T> bool isSmaller(T fir, T sec){ return fir < sec; } template class std::vector<int>; // (1) template bool std::vector<double>::empty() const; // (2) template class MyClass<int>; // (3) template std::string MyClass<double>::getType() const; // (4) template bool isSmaller(int, int); // (5) template bool isSmaller<double>(double, double); // (6) int main(){ std::cout << '\n'; std::cout << std::boolalpha; std::vector vec{1, 2, 3, 4, 5}; std::cout << "vec.size(): " << vec.size() << '\n'; MyClass myClass(5); std::cout << "myClass.getType(): " << myClass.getType() << '\n'; std::cout << '\n'; std::cout << "isSmaller(5, 10): " << isSmaller(5,10) << '\n'; std::cout << "isSmaller<double>(5.5f, 6.5): " << isSmaller<double>(5.5f, 6.5) << '\n'; std::cout << '\n'; }
行 (1) から (6) は興味深いものです。キーワード template
のおかげで 、明示的な template
インスタンス化が行われます。
- 行 (1) で
std::vector
を明示的にインスタンス化int
の場合 行 (2) そのメンバー関数empty
double.
の場合 - 行 (3) は
MyClass
を明示的にインスタンス化しますint
の場合 行 (4) そのメンバー関数getType
double
の場合 . - 行 (5) で明示的に
isSmaller
をインスタンス化(int, int)
の場合 、および行 (6) は(double, double)
に対して同じことを行います 明示的なテンプレート引数double
を提供する .
テンプレートの実装を非表示
明示的なテンプレートのインスタンス化は、テンプレートの定義を隠すのにどのように役立ちますか?
- テンプレート宣言をヘッダー ファイルに入れます。
- テンプレート定義をソース ファイルに入れます。ソース ファイルの最後でテンプレートを明示的にインスタンス化します。
- ヘッダー ファイルを含めてテンプレートを使用します。
このプロセスを例示する 3 つのファイルを次に示します。
- テンプレート宣言
// MyClass.h #include <typeinfo> #include <string> template <typename T> class MyClass{ public: MyClass(T t) { } std::string getType() const; };
int
のテンプレート定義と明示的なインスタンス化
// MyClass.cpp #include "MyClass.h" template <typename T> std::string MyClass<T>::getType() const { return typeid(T).name(); } template class MyClass<int>;
- テンプレートの使用
// mainMyClass.cpp #include "MyClass.h" #include <iostream> int main() { std::cout << '\n'; MyClass myClass(5); std::cout << "myClass.getType(): " << myClass.getType() << '\n'; /* MyClass myClass2(5.5); std::cout << "myClass2.getType(): " << myClass2.getType() << '\n'; */ std::cout << '\n'; }
プログラムをコンパイルして実行すると、期待どおりの結果が得られます。
しかし、 MyClass
を使おうとすると int
以外のタイプの場合 、リンカーエラーが発生します。これは、コメントアウトされた行を使用したときに表示されるリンカー エラー メッセージです。
double
のテンプレートのインスタンス化はありません
テンプレートのインスタンス化を抑制する
MyClass<int
を使用するとします。> リンカーがまとめたさまざまな翻訳単位で。基本的に、リンカーはテンプレートのインスタンス化を 1 つを除いてすべて破棄します。これは計算時間の無駄です。 C++11 での extern キーワードの使用のおかげで、明示的なテンプレートのインスタンス化定義から明示的なテンプレートのインスタンス化宣言を作成できます。
template class MyClass<int>; // explicit instantiation definition extern template class MyClass<int>; // explicit instantiation declaration
重要な観察事項は、2 行目ではテンプレートのインスタンス化が発生しないことです。これは、リンカーが破棄するものではなく、コンパイラが生成することを意味します。 MyClass<int>
の 1 つのインスタンス化を確実にする必要があるだけです。 利用可能なリンカー用です。そうでない場合、リンカー エラーが発生します。
次は?
このより技術的な投稿の後、次の投稿で可変個引数テンプレートについて書きます ... .