题目

编写程序,演示 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 |

验证:整个矩阵计算完全在栈上就地展开,零堆内存分配!