Tmeplate parameters in C++ are not limited to types—they can also be compile-time constants, known as non-type (or numeric) template parameters. These enable powerful compile-time computations and fixed-size data structures.
Constraints on Non-Type Template Parameters
- Must be compile-time constants—variables are not allowed.
- Floaitng-point values cannot be used.
- Class instances or pointers (except for specific cases like pointer-to-member) are disallowed.
- Only integral, enumeration, or pointer/reference types with external linkage qualify.
Compile-Time Computation Example
The following demonstrates recursive template instantiation to compute the sum 1 + 2 + ... + N at compile time:
#include <iostream>
template <int N>
class CompileTimeSum {
public:
static const int VALUE = CompileTimeSum<N - 1>::VALUE + N;
};
template <>
class CompileTimeSum<1> {
public:
static const int VALUE = 1;
};
int main() {
std::cout << "Sum 1..10 = " << CompileTimeSum<10>::VALUE << '\n';
std::cout << "Sum 1..100 = " << CompileTimeSum<100>::VALUE << '\n';
return 0;
}
Each recursive instentiation generates a distinct class type (CompileTimeSum<100>, CompileTimeSum<99>, etc.), but modern compilers optimize away unused intermediate types, retaining only the final computed constant.
Fixed-Size Array Template
A stack-allocated array class using a numeric template parameter for size:
// Array.h
#ifndef ARRAY_H
#define ARRAY_H
template <typename T, int Size>
class FixedArray {
protected:
T elements[Size];
public:
int length() { return Size; }
int length() const { return Size; }
bool get(int index, T& value) {
if (index >= 0 && index < Size) {
value = elements[index];
return true;
}
return false;
}
bool set(int index, T value) {
if (index >= 0 && index < Size) {
elements[index] = value;
return true;
}
return false;
}
T& operator[](int index) { return elements[index]; }
virtual ~FixedArray() = default;
};
#endif
Heap-Allocated Dynamic Array Wrapper
A factory-based heap array that encapsulates dynamic allocation:
// HeapArray.h
#ifndef HEAP_ARRAY_H
#define HEAP_ARRAY_H
template <typename T>
class HeapArray {
private:
T* data;
int len;
HeapArray(int n) : len(n), data(nullptr) {}
bool initialize() { return (data = new(std::nothrow) T[len]) != nullptr; }
public:
static HeapArray* create(int n) {
auto instance = new(std::nothrow) HeapArray(n);
if (instance && instance->initialize()) {
return instance;
}
delete instance;
return nullptr;
}
int length() const { return len; }
T& operator[](int i) { return data[i]; }
bool get(int i, T& val) {
if (i >= 0 && i < len) {
val = data[i];
return true;
}
return false;
}
bool set(int i, const T& val) {
if (i >= 0 && i < len) {
data[i] = val;
return true;
}
return false;
}
HeapArray& self() { return *this; }
~HeapArray() { delete[] data; }
};
#endif
Usage Example
#include <iostream>
#include "Array.h"
#include "HeapArray.h"
int main() {
FixedArray<double, 5> arr1;
for (int i = 0; i < arr1.length(); ++i)
arr1[i] = i + 1;
for (int i = 0; i < arr1.length(); ++i)
std::cout << arr1[i] << '\n';
const FixedArray<int, 10> arr2;
std::cout << "Const array length: " << arr2.length() << '\n';
auto* heapArr = HeapArray<int>::create(15);
if (heapArr) {
auto& arr = heapArr->self();
for (int i = 0; i < arr.length(); ++i)
arr[i] = i + 1;
for (int i = 0; i < arr.length(); ++i)
std::cout << arr[i] << '\n';
delete heapArr;
}
return 0;
}