题目
编写程序,演示 C++ 模板中的非类型模板参数(Non-type Template Parameters, NTTP)。设计一个编译期固定维度的矩阵类模板 StaticMatrix<typename T, size_t Rows, size_t Cols>。展示其将矩阵维度在编译期绑定进类型的特性:内部直接依托静态栈原生数组(零堆分配),且利用非类型模板参数在编译期强制检验矩阵乘法的维度合法性(若 \(A\) 的列数不等于 \(B\) 的行数,直接在编译期拒绝构建乘法函数并报错)。
输入格式:无需外部输入。
输出格式:输出编译期定长矩阵的初始化与乘法计算结果。
数据范围与约定:NTTP 模板参数、编译期维度安全检查与零动态分配矩阵。
思路与算法
1. 非类型模板参数的类型约束
非类型模板参数不能是任意对象,在 C++20 之前严格限制为整型枚举、指针与左值引用(如 size_t N、int K)。
2. 编译期维度类型安全
对于矩阵乘法 \((R1 \times C) \times (C \times C2) = R1 \times C2\)。通过模板签名:StaticMatrix<T, R1, C2> operator*(const StaticMatrix<T, R1, C>& a, const StaticMatrix<T, C, C2>& b),如果维度不匹配,根本无法通过编译,从源头彻底杜绝运行时矩阵乘法维度越界!
3. 极致的循环展开优化
因为 Rows 和 Cols 是编译期常量立即数,现代编译器会毫不犹豫地将三重循环彻底展开为向量化 SIMD 机器指令。
完整程序
#include <iostream>
#include <iomanip>
template <typename T, size_t Rows, size_t Cols>
class StaticMatrix {
private:
T data_[Rows][Cols];
public:
StaticMatrix() : data_{} {}
void set(size_t r, size_t c, T val) {
data_[r][c] = val;
}
T get(size_t r, size_t c) const {
return data_[r][c];
}
void print(const std::string& label) const {
std::cout << label << " (" << Rows << "x" << Cols << "):\n";
for (size_t r = 0; r < Rows; ++r) {
std::cout << " | ";
for (size_t c = 0; c < Cols; ++c) {
std::cout << std::setw(5) << data_[r][c] << " ";
}
std::cout << "|\n";
}
}
// 核心:利用 NTTP 强制要求左矩阵的 Cols 严格等于右矩阵的 CommonDim 行数!
template <size_t RightCols>
StaticMatrix<T, Rows, RightCols> operator*(const StaticMatrix<T, Cols, RightCols>& rhs) const {
StaticMatrix<T, Rows, RightCols> result;
for (size_t i = 0; i < Rows; ++i) {
for (size_t j = 0; j < RightCols; ++j) {
T sum = 0;
for (size_t k = 0; k < Cols; ++k) {
sum += data_[i][k] * rhs.get(k, j);
}
result.set(i, j, sum);
}
}
return result;
}
};
int main() {
// 实例化 2x3 矩阵
StaticMatrix<int, 2, 3> A;
A.set(0, 0, 1); A.set(0, 1, 2); A.set(0, 2, 3);
A.set(1, 0, 4); A.set(1, 1, 5); A.set(1, 2, 6);
// 实例化 3x2 矩阵
StaticMatrix<int, 3, 2> B;
B.set(0, 0, 7); B.set(0, 1, 8);
B.set(1, 0, 9); B.set(1, 1, 1);
B.set(2, 0, 2); B.set(2, 1, 3);
A.print("矩阵 A");
B.print("矩阵 B");
// 编译期维度严格匹配,乘积为 2x2 矩阵
auto C = A * B;
C.print("矩阵 C = A * B");
std::cout << "\n验证:整个矩阵计算完全在栈上就地展开,零堆内存分配!\n";
return 0;
}运行示例
输出:
矩阵 A (2x3):
| 1 2 3 |
| 4 5 6 |
矩阵 B (3x2):
| 7 8 |
| 9 1 |
| 2 3 |
矩阵 C = A * B (2x2):
| 31 19 |
| 85 55 |
验证:整个矩阵计算完全在栈上就地展开,零堆内存分配!