理解C++标准库[STL]的type_traits的实现原理,如何在实际开发中使用类型特性做算法优化。

背景

  • 问题实例】我们先看一个问题,对于n阶矩阵如何计算它的m次幂?
    学过线性代数的朋友都知道,A^m = A*A*A*...*A(一共m个A相乘)。
    n阶矩阵A*A的时间复杂度为O(n*n*n);而A^m次方的复杂度为O(n*n*n)^m = O(n^3(m-1));这个复杂度有点高,对于具有特殊属性的矩阵,比如实对称矩阵,这种情况下,复杂度是否可以降下来呢,当然可以,我们可以用矩阵的相似对角化将矩阵A分解为A = inv(P)*Diag*P;此时A^m = inv(P)*Diag^m*P的复杂度可以降低到O(n^7*m); [这块复杂度分析不正确,读者可以大概理解下我想表达的意思:根据类型特性确实可以起到算法优化的目的]

  • 解决思路
    1. 通用解法:老老实实按照矩阵乘法算, 适用于任何n阶矩阵,缺点效率低下
    2. 高效解法:对于实对称矩阵,可以相似对角化; 优点:可以针对性优化,效率贼高

  • C++解决方案】 模板编程:编译时候多态,效率高且对程序员友好; 如果用动多态技术的话,运行时效率会有问题,对于数值问题,不可取。

型别特性

  • type trait这里的type可以简单等同为class,但二者并不完全一致,type侧重接口,而class侧重实现。这里不做区分。 type_trait == class_trait
  • 在进入实例之前我们先建立一个概念,type traits或叫型别特性是什么意思,我们看下C++之父的一段话:

Think of a trait as a small object whose main purpose is to carry information used by another object or algorithm to determine “policy” or “implementation details”.

  • Bjarne Stroustrup
  • Bjarne Stroustrup大神说,型别特性是一个小对象,其主要目的是携带关于该型别的特性信息,这些信息会被另外的对象或者算法使用,来决定对象或算法自己的策略或实现细节。

矩阵求幂解决方案

1. 定义两种类型,用于标识实对称矩阵和普通矩阵

// 型别类型定义,没有任何成员定义
// 普通矩阵型别
struct GeneralMatrixCategory_type {};

// 实对称矩阵型别
struct RealSymmetricMatrixCategory_type {};

2. 定义型别特性模板类,用于矩阵型别特性萃取

// 矩阵型别特性萃取类
template <typename Matrix>
struct matrix_traits {
  // 这里有个小小契约,所有矩阵类型必须有矩阵所属型别类型定义 category_type
  typedef typename Matrix::category_type  value_type;
};

3. 定义两种类型矩阵

  • 两种矩阵都具有类型重定义类型别名 category_type
  • 为了可以和后续的算法pow搭配,任何用户自定义的矩阵类型都需要定义类型别名 category_type
template <typename Tp>
class RealSymmetricMatrix {
public:
  // 具有 RealSymmetricMatrixCategory_typ 的型别
  typedef RealSymmetricMatrixCategory_type category_type;

public:
  RealSymmetricMatrix(std::size_t  dim) : dim_(dim) { }

private:
  Tp* buffer_ = nullptr;
  std::size_t  dim_;
};

template <typename Tp>
class GeneralMatrix {
public:
  // 具有 GeneralMatrixCategory_type 的型别
  typedef GeneralMatrixCategory_type category_type;

public:
  GeneralMatrix(std::size_t dim) : dim_(dim) {}

private:
  Tp* buffer_ = nullptr;
  std::size_t  dim_;
};

4. 两个辅助函数

  • 这里为什么是两个?因为只用两种矩阵型别类型定义。
template <typename Matrix>
Matrix __pow(const Matrix &m, size_t n, GeneralMatrixCategory_type) {
  std::cout << "通用版本被调用" << std::endl;
}
template <typename Matrix>
Matrix __pow(const Matrix &m, size_t n, RealSymmetricMatrixCategory_type) {
  std::cout << "高效版本被调用" << std::endl;
}

5. 用户接口

/**
 * 计算矩阵m的n次方.
 *
 * @tparam Tp : scalar<int double>
 * @param m
 * @param n
 * @return
 */
template <typename Matrix> Matrix pow(const Matrix &m, size_t n) {
  typedef typename matrix_traits<Matrix>::value_type value_type;
  __pow(m, n, value_type()); // 这里创建了一个匿名对象,编译器会自动进行类型推倒,进而调用两个辅助函数中的一个。
}

展望

  • 可以加入别的矩阵类型,如
struct SparseMatrixCategory_type {};
  • 并引入继承关系
struct SparseMatrixCategory_type : public GeneralMatrixCategory_type {};

struct RealSymmetricMatrixCategory_type {};
  • 对于这种情况,如果有新的矩阵类型定义,比如
template <typename Tp>
class SparseMatrix {
public:
  // 具有 SparseMatrixCategory_type 的型别
  typedef SparseMatrixCategory_type category_type;

public:
  SparseMatrix(std::size_t dim) : dim_(dim) {}

private:
  Tp* buffer_ = nullptr;
  std::size_t  dim_;
};
  • 即使没有专门针对稀疏矩阵求幂的算法实现,pow也可以用最普通版本的算法来计算稀疏矩阵求幂。这是由于继承关系的原因。

说明

  • 两个矩阵类型的定义并不完整,这里关注的不是类本身的定义,而是算法如何自动选择最高效的实现版本来计算矩阵的m次幂。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值