1. 導入
C++のテンプレートといえば、型(Type)をパラメータ化するイメージが強いですが、数値やポインタといった「値」をパラメータ化することも可能です。これが「非型テンプレート引数(Non-type template parameters: NTTP)」です。この技術を活用することで、実行時のオーバーヘッドを排除し、コンパイル時にメモリサイズやアルゴリズムの挙動を決定できるため、パフォーマンスが求められるシステム開発やライブラリ設計において非常に重要な役割を果たします。
2. 基礎知識
通常、テンプレートは template
使用可能な型には以下の制限があります。
・整数型(int, char, bool, longなど)
・列挙型(enum)
・ポインタや参照(オブジェクト、関数、メンバへのポインタ)
・std::nullptr_t
※C++17以降では浮動小数点数や構造体も一部条件付きで使用可能になりましたが、基本的には整数や列挙型が最も一般的で安定しています。
3. 実装/解決策
非型テンプレート引数を使う主なメリットは「コンパイル時定数」として扱える点です。例えば、動的にメモリを確保するのではなく、固定長の配列のサイズをテンプレート引数で指定することで、ヒープ領域へのアクセスを回避し、スタック領域で高速に処理を完了させることができます。
4. サンプルプログラム
以下は、サイズをテンプレート引数で指定可能な、固定長バッファクラスの例です。
include
include
// Nを非型テンプレート引数として受け取る
template
class StaticBuffer {
private:
T data[N]; // コンパイル時にサイズが決定されるため非常に高速
public:
void set(std::size_t index, T value) {
if (index < N) {
data[index] = value;
}
}
T get(std::size_t index) const {
return (index < N) ? data[index] : T();
}
// バッファサイズをコンパイル時に取得可能
constexpr std::size_t size() const { return N; }
};
int main() {
// コンパイル時にサイズ10のint配列を持つオブジェクトを生成
StaticBuffer
buffer.set(0, 100);
std::cout << "値: " << buffer.get(0) << std::endl;
std::cout << "バッファサイズ: " << buffer.size() << std::endl;
return 0;
}
5. 応用・注意点
現場で活用する際の注意点は、テンプレートのインスタンス化によるコード肥大化です。例えば、サイズNごとに別々のクラスとしてコンパイルされるため、多種多様なサイズでインスタンス化しすぎると、バイナリサイズが急激に増大する可能性があります。
また、C++20以降ではクラス型も非型テンプレート引数として使用できるようになりましたが、複雑なオブジェクトを渡すとコンパイル時間が大幅に伸びる傾向があります。基本的には、パフォーマンスがクリティカルな箇所のバッファサイズ調整や、フラグの切り替えなどに限定して使用するのが、保守性を保つコツです。

コメント