テンプレートを含むソースファイルをヘッダーファイルから分離する
重要なのは、テンプレートの明示的なインスタンス化にあります。
解決
ソースファイルを分離するテンプレート機能
- 通常の方法で書き込み
.h
と.cpp
ファイルを行いますが、追加されたtemplate <typename T>
ステートメントに注意してください。 .cpp
ファイルの最後に显式实例化
、次のようなテンプレートコードを追加します。
// 显式实例化,注意,`template`之后没有`<>`,若添加则会报错!
// 每种将被使用的类型,均需要进行显式实例化
template void A::show(int &&);
template void A::show(double &&);
template void A::show(bool &&);
テンプレートクラスの個別のソースファイル
- テンプレートクラス
.h
と.cpp
ファイルを別々にtemplate <typename T>
記述し、追加されたステートメントに注意してください。 .cpp
ファイルの最後に显式实例化
、次のようなクラスのコードを追加します。
// 只需要class的显式实例化即可,不需要再写其成员函数的显式实例化代码
template class Base<QLabel>;
- このテンプレートクラスを使用する他のクラスの
.h
ファイルの最後に、外部テンプレートクラスを追加します显式实例化声明
(extern
キーワードをもう1つ追加)。
// 添加这行代码的目的,其实是为了消除编译警告。即便删掉它,也不会导致编译错误
extern template class Base<QLabel>;
以上は、テンプレート関数・クラスのソースファイルとヘッダーファイルを分離する方法の簡単な説明でしたが、記載漏れがあることは避けられません。それでも質問がある場合は、特定のケースを引き続き確認できます。
サンプルコード
テンプレート機能
- 番目
#ifndef _T_H
#define _T_H
class A
{
public:
template <typename T>
void show(T &&t);
};
#endif // _T_H
- t.cpp
#include "t.h"
#include <iostream>
using namespace std;
template <typename T>
void A::show(T &&t)
{
string info = typeid(t).name();
cout << info << endl;
}
// 显式实例化,注意,`template`之后没有`<>`,若添加则会报错!
template void A::show(int &&);
template void A::show(double &&);
template void A::show(bool &&);
- main.cpp
#include <iostream>
#include "t.h"
using namespace std;
int main()
{
A a;
a.show(1);
a.show(3.4);
a.show(true);
}
直接コンパイルするだけです:
a: t.cpp main.cpp
g++ $^ -o $@ -std=c++17
実行:
./a
```output
i
d
b
```end
テンプレートクラス
テンプレートクラス(基本クラス)
- ヘッドファイル
#include <QObject>
class QLabel;
template <typename T>
class Base : public QObject
{
public:
Base(QObject *parent = nullptr);
virtual ~Base() override;
void add(T *t);
void remove(T *t);
void remove(int idx);
T *find(int idx) const;
protected:
QList<T *> _listItems;
};
- ソースファイル
#include "base.h"
#include <QLabel>
template <typename T>
Base<T>::Base(QObject *parent)
: QObject(parent)
{
// Do nothing
}
template <typename T>
Base<T>::~Base()
{
qDeleteAll(_listItems);
_listItems.clear();
}
template <typename T>
void Base<T>::add(T *t)
{
_listItems.append(t);
}
template <typename T>
void Base<T>::remove(T *t)
{
_listItems.removeAll(t);
}
template <typename T>
void Base<T>::remove(int idx)
{
_listItems.removeAt(idx);
}
template <typename T>
T *Base<T>::find(int idx) const
{
return _listItems.at(idx);
}
/************************************ Explicit Instantiate *****************************/
template class Base<QLabel>;
テンプレートクラスの通常の派生クラス
- ヘッドファイル
#include "base.h"
class QLabel;
class Son : public Base<QLabel>
{
public:
Son(QObject *parent = nullptr);
virtual ~Son() override;
protected:
void init();
};
/********************************** Explicit Instantiate *****************************/
extern template class Base<QLabel>;
- ソースファイル
#include "son.h"
#include <QLabel>
Son::Son(QObject *parent)
: Base<QLabel>(parent)
{
init();
}
Son::~Son()
{
// Do nothing
}
void Son::init()
{
add(new QLabel());
remove(new QLabel);
remove(123);
find(321);
}
テンプレートから派生したクラスからコンパイラの警告を排除する方法
- テンプレートクラスbase classの
cpp
ファイルの最後に、明示的なインスタンス化コードを追加します。
template class Base<QLabel>;
- テンプレートクラスbase classの
.cpp
ファイルの先頭に、インクルードファイルを追加します。
#include <QLabel>
- テンプレートクラスの一般的な派生クラスの
.h
ファイルの最後に外部宣言コードを追加します。
extern template class Base<QLabel>;