模板(template)是 C++ 最强大的特性之一,也是理解 Eigen 的关键。它让你写一套代码,适用所有类型。本章从"为什么需要模板"讲起,一步步理解模板,最后揭示 Eigen 类型的真面目。
14.1 问题:重复的代码
想写一个"返回较大值"的函数,要支持 int、double:
intmaxInt(inta,intb){returna>b?a:b;}doublemaxDouble(doublea,doubleb){returna>b?a:b;}逻辑完全一样,只是类型不同,却要写两遍。类型一多,代码会爆炸式重复。
14.2 函数模板:一套代码适用多类型
用函数模板,把类型"参数化":
#include<iostream>template<typenameT>// 声明模板,T 是"类型参数"Tmax(T a,T b){returna>b?a:b;}intmain(){std::cout<<max(3,5)<<std::endl;// T 推导为 int,5std::cout<<max(3.5,1.5)<<std::endl;// T 推导为 double,3.5std::cout<<max('a','z')<<std::endl;// T 推导为 char,z}拆解template <typename T>:
template:声明"这是模板"typename T:T是一个类型占位符,调用时会被替换成实际类型
调用max(3, 5)时,编译器自动把T替换成int,生成一个int版本的函数。这个过程叫模板实例化。
可以理解成:模板是"模具",调用时用实际类型"浇铸"出具体函数。
T可以换成int、double、string、甚至自定义类。
14.3 类模板:类型也可作类的参数
类也能参数化。比如一个"坐标点"类,坐标类型可以是 int 也可以是 double:
#include<iostream>template<typenameT>structPoint{T x,y;TlengthSq(){// 返回 x² + y²returnx*x+y*y;}};intmain(){Point<int>p1{3,4};// int 版std::cout<<p1.lengthSq()<<std::endl;// 25Point<double>p2{2.5,3.0};// double 版std::cout<<p2.lengthSq()<<std::endl;// 15.25}Point<int>和Point<double>是两个不同的类型,编译器分别生成。
14.4 模板可以有多个参数
template<typenameT,intN>// 类型参数 + 非类型参数structArray{T data[N];};Array<int,5>a;// 装 5 个 intArray<double,10>b;// 装 10 个 doubleN是非类型参数(一个具体的值,如 5、10),在编译期确定。
14.5 揭开 Eigen 类型的真面目
现在,Eigen 的类型名就能看懂了。Eigen 的矩阵是这么定义的:
Eigen::Matrix<double,3,3>// 全称:元素 double,3 行,3 列这正是类模板!Matrix是模板,<double, 3, 3>是模板参数(元素类型 + 行数 + 列数)。
那些Matrix3d、Vector3d之类,其实是别名(typedef):
usingnamespaceEigen;// 先"打开"Eigen,下面才能直接写 Matrix、DynamicusingMatrix3d=Matrix<double,3,3>;// 等价别名usingVector3d=Matrix<double,3,1>;// 3x1 的矩阵usingMatrixXd=Matrix<double,Dynamic,Dynamic>;// 动态大小这三行就是 Eigen 自己定义别名时写的代码(它写在namespace Eigen里面,所以你平时不用写using namespace Eigen;也能用Eigen::Matrix3d)。
所以:
Eigen::Matrix3d m;// 写法一:用别名Eigen::Matrix<double,3,3>n;// 写法二:把模板参数展开写n=m;// 能直接赋值——说明两者是同一个类型Matrix3d里的3d= “3 行 3 列 double”MatrixXd里的X= Dynamic(动态大小),d= doubleVector3d= 3 行 1 列的 double 矩阵(列向量)
这就是 Eigen 强大的根源:一个
Matrix<Scalar, Rows, Cols>模板,配合不同参数,就产生了 Matrix3d、MatrixXd、Vector3d、VectorXd 等几十种类型,还能扩展到你自定义的类型。模板让 Eigen 用一套代码支持一切。
14.6 模板函数实战:打印任意矩阵
#include<iostream>#include<Eigen/Dense>// 模板函数:能打印任意 Eigen 矩阵template<typenameDerived>voidprintMat(constEigen::MatrixBase<Derived>&m){std::cout<<"矩阵 ("<<m.rows()<<"x"<<m.cols()<<"):\n"<<m<<std::endl;}intmain(){Eigen::Matrix3d m1=Eigen::Matrix3d::Identity();Eigen::VectorXd v2=Eigen::VectorXd::LinSpaced(5,0,4);printMat(m1);// 3x3printMat(v2);// 5x1}这个函数能打印任何大小、任何类型的 Eigen 矩阵——这就是模板的力量。
实际输出:
矩阵 (3x3): 1 0 0 0 1 0 0 0 1 矩阵 (5x1): 0 1 2 3 4printMat里用了m.rows()、m.cols(),还用了Eigen::MatrixBase<Derived>这种"接住任何 Eigen 表达式"的写法。第 23 章会解释为什么参数要写成MatrixBase<Derived>而不是直接写MatrixXd。
14.7 小结
- 模板 = 类型参数化,一套代码适用多类型。
- 函数模板
template <typename T>,调用时自动推导实例化。 - 类模板
template <typename T> class X,X<int>、X<double>是不同类型。 - Eigen 的
Matrix<Scalar, Rows, Cols>是类模板,Matrix3d等是别名。 - 模板是 Eigen 灵活性的根源。
下一章,学习现代 C++ 的实用特性,让代码更简洁。
练习题
- 写函数模板
swap交换任意两个同类型的值。 - 写类模板
Pair<T, U>,存两个不同类型的值。 - 写函数模板
sum,求一个std::vector<T>所有元素的和。 - 用
Eigen::Matrix<double, 3, 3>和Eigen::Matrix3d各声明一个矩阵,验证它们等价。 - 用自己的话解释"模板实例化"是什么。