C++高级应用(1)-类型擦除(Type Erasure)和模板特化(Template Specialization)详解
类型擦除(Type Erasure)和模板特化(Template Specialization)是 C++ 中两个非常强大的泛型编程机制。它们可以分别用于构建灵活的抽象接口和控制模板行为。我们将分别讲解这两个机制的原理、用法以及结合应用示例。
一、类型擦除(Type Erasure)
原理概念
类型擦除是一种设计技巧,用于隐藏类型信息而保留其行为,使得用户代码可以操作任意类型的对象,只要它满足某些接口要求。例如:
- 标准库中的
std::function
std::any
/std::variant
- 自定义泛型容器 / 接口桥接器
示例:模拟 std::function
的类型擦除
实现一个 AnyCallable
,它可以存储任意可调用对象(函数、lambda、仿函数等)。
#include <iostream>
#include <memory>class AnyCallable {
public:// 构造函数模板,接受任意可调用对象template<typename Callable>AnyCallable(Callable func) : obj_(std::make_unique<Model<Callable>>(std::move(func))) {}// 调用包装的函数void operator()() const {obj_->call();}private:struct Concept {virtual ~Concept() = default;virtual void call() const = 0;};template<typename Callable>struct Model : Concept {Callable func_;Model(Callable f) : func_(std::move(f)) {}void call() const override {func_();}};std::unique_ptr<Concept> obj_;
};int main() {AnyCallable f1 = []() { std::cout << "Hello Lambda\n"; };AnyCallable f2 = []() { std::cout << "Another function\n"; };f1();f2();return 0;
}
重点:
Concept
是一个抽象基类(接口)Model<T>
是具体类型实现,模板化实现任意类型- 所有类型都被“擦除”成
Concept*
接口操作
二、模板特化(Template Specialization)
模板特化是控制模板行为的工具,可以为特定类型或条件提供特殊实现。
分类:
- 全特化(Full Specialization)
- 偏特化(Partial Specialization)
- SFINAE/
std::enable_if
/constexpr if
条件特化
示例 1:全特化
template<typename T>
struct Printer {void print() { std::cout << "Generic type\n"; }
};template<>
struct Printer<int> {void print() { std::cout << "Int specialization\n"; }
};int main() {Printer<double>().print(); // GenericPrinter<int>().print(); // Int specialization
}
示例 2:偏特化
template<typename T1, typename T2>
struct Pair {};template<typename T>
struct Pair<T, int> {void print() { std::cout << "Second is int\n"; }
};int main() {Pair<double, int>().print(); // Second is int
}
三、二者结合:类型擦除 + 模板特化示例
用一个统一接口 PointContainer
来封装不同类型的点云容器(如 std::vector<Eigen::Vector3d>
、pcl::PointCloud<pcl::PointXYZ>
等),并通过特化获取其大小、访问等。
示例:简化版点容器类型擦除 + 特化访问器
#include <iostream>
#include <vector>
#include <memory>
#include <Eigen/Dense>// 类型擦除接口
class IPointContainer {
public:virtual ~IPointContainer() = default;virtual size_t size() const = 0;virtual Eigen::Vector3d point(size_t i) const = 0;
};// 模板实现(通过特化支持不同容器)
template<typename T>
class PointContainerImpl : public IPointContainer {
public:PointContainerImpl(T data) : data_(std::move(data)) {}size_t size() const override {return traits<T>::size(data_);}Eigen::Vector3d point(size_t i) const override {return traits<T>::point(data_, i);}private:T data_;
};// traits 特化
template<typename T>
struct traits;template<>
struct traits<std::vector<Eigen::Vector3d>> {static size_t size(const std::vector<Eigen::Vector3d>& v) {return v.size();}static Eigen::Vector3d point(const std::vector<Eigen::Vector3d>& v, size_t i) {return v[i];}
};// 使用统一接口
int main() {std::vector<Eigen::Vector3d> pts = {{1, 2, 3},{4, 5, 6}};std::unique_ptr<IPointContainer> container = std::make_unique<PointContainerImpl<std::vector<Eigen::Vector3d>>>(pts);for (size_t i = 0; i < container->size(); ++i) {std::cout << "Point: " << container->point(i).transpose() << "\n";}
}
总结对比:
特性 | 类型擦除 | 模板特化 |
---|---|---|
目的 | 抽象接口,隐藏实现类型 | 为特定类型提供自定义实现 |
实现机制 | 虚函数 + 模板组合 | template<> / std::enable_if 等 |
灵活性 | 高,运行时处理多种类型 | 高,编译期选择不同实现 |
成本 | 有运行时开销(虚函数) | 无运行时开销,编译期展开 |
使用场景 | 插件式架构、多态容器、API封装 | 数值类型优化、特例算法、高性能分支 |