C++模板作为泛型编程的核心工具,其本质是一种编译时多态技术。与运行时多态不同,模板在编译阶段就完成了类型特化和代码生成,这使得它成为性能敏感场景的理想选择。理解模板的工作机制是高效使用它的前提。
当编译器遇到模板使用时,会执行实例化过程。以简单的栈模板为例:
cpp复制template<typename T>
class Stack {
public:
void push(const T& item) { /* 实现 */ }
T pop() { /* 实现 */ }
private:
T* data;
// 其他成员...
};
Stack<int> intStack; // 显式实例化int版本
实例化过程分为三个阶段:
关键特性在于惰性实例化——只有被实际使用的成员函数才会生成代码。例如:
cpp复制template<typename T>
class Logger {
public:
void log(const T& msg) { /* 实现 */ }
void saveToFile() { /* 实现,可能依赖T的特性 */ }
};
Logger<int> intLogger;
intLogger.log(42); // 仅实例化log函数
// saveToFile未被使用,不会实例化
传统上模板定义放在头文件中,但这并非唯一选择。现代C++提供了多种组织方式:
方案一:经典头文件方式
cpp复制// stack.h
template<typename T>
class Stack {
public:
void push(const T&);
// 声明
};
template<typename T>
void Stack<T>::push(const T& item) {
// 实现
}
方案二:显式实例化分离
cpp复制// stack.h
template<typename T> class Stack { /* 声明 */ };
// stack.cpp
template<typename T>
void Stack<T>::push(const T&) { /* 实现 */ }
// 显式实例化常用类型
template class Stack<int>;
template class Stack<std::string>;
方案三:C++20模块化模板
cpp复制// stack.ixx
export module stack;
export template<typename T>
class Stack {
// 完整定义
};
提示:对于大型项目,方案二能显著减少编译时间,但需要预先知道所有可能用到的类型。方案三是最新的模块化方式,能提供更好的隔离性。
代码膨胀是模板使用中最常见的性能问题,表现为二进制文件中出现大量相似代码。这种现象源于模板为每种类型参数生成独立代码的特性。
膨胀主要发生在以下场景:
vector<int>和vector<float>生成几乎相同的代码实测案例:一个包含10个方法的模板类,当实例化5种不同类型时:
识别模板中不依赖类型参数的部分,将其移出模板:
cpp复制// 优化前:全部在模板内
template<typename T>
class DataProcessor {
public:
void process(T data) {
setupEnvironment(); // 不依赖T
// ...类型相关处理
cleanup(); // 不依赖T
}
private:
void setupEnvironment() { /* 实现 */ }
void cleanup() { /* 实现 */ }
};
// 优化后:提取非类型相关代码
class ProcessorBase {
protected:
void setupEnvironment() { /* 实现 */ }
void cleanup() { /* 实现 */ }
};
template<typename T>
class DataProcessor : private ProcessorBase {
public:
void process(T data) {
setupEnvironment();
// ...类型相关处理
cleanup();
}
};
对于已知会用到的类型,集中进行显式实例化:
cpp复制// 在.cpp文件中
template class std::vector<int>;
template class std::vector<float>;
template class std::vector<double>;
结合运行时多态减少实例化数量:
cpp复制class AnyPrinter {
public:
template<typename T>
AnyPrinter(T obj) :
self_(std::make_shared<Model<T>>(std::move(obj))) {}
void print() const { self_->print(); }
private:
struct Concept {
virtual ~Concept() = default;
virtual void print() const = 0;
};
template<typename T>
struct Model : Concept {
Model(T obj) : data(std::move(obj)) {}
void print() const override { /* 实现 */ }
T data;
};
std::shared_ptr<const Concept> self_;
};
在资源受限的嵌入式系统中,模板优化尤为重要:
ROM空间优化:
-ffunction-sections和-fdata-sections编译选项--gc-sections链接器选项移除未使用代码模板特化策略:
cpp复制template<>
class Stack<bool> {
// 位压缩特化版本
};
内存分配控制:
cpp复制template<typename T>
class EmbeddedAllocator {
// 定制内存分配策略
};
using SafeVector = std::vector<int, EmbeddedAllocator<int>>;
实测数据表明,经过优化的模板代码在ARM Cortex-M系列处理器上可减少30%-50%的代码体积,同时保持相同的性能表现。
类型安全是模板相对于void*等传统技术的核心优势。通过编译时类型检查,可以消除大量运行时错误。
基于void*的传统容器存在严重类型安全问题:
cpp复制// 传统不安全实现
void push(void* item);
void* pop();
模板包装器方案提供零开销类型安全:
cpp复制template<typename T>
class SafeStack {
public:
void push(T* item) { impl_.push(item); }
T* pop() { return static_cast<T*>(impl_.pop()); }
private:
UnsafeStack impl_; // 基于void*的实现
};
进阶技巧:使用私有继承防止直接访问底层实现
cpp复制class UnsafeStack {
protected: // 注意是protected
void pushImpl(void*);
void* popImpl();
};
template<typename T>
class SafeStack : private UnsafeStack {
public:
void push(T* item) { pushImpl(item); }
T* pop() { return static_cast<T*>(popImpl()); }
};
cpp复制template<typename T>
class NumericQueue {
static_assert(std::is_arithmetic_v<T>,
"NumericQueue requires arithmetic types");
// 实现...
};
cpp复制template<typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::same_as<T>;
};
template<Addable T>
class Calculator {
// 实现...
};
cpp复制struct TagA {};
struct TagB {};
template<typename T>
void processImpl(T data, TagA);
template<typename T>
void processImpl(T data, TagB);
template<typename T>
void process(T data) {
if constexpr (/* 条件 */)
processImpl(data, TagA{});
else
processImpl(data, TagB{});
}
在数据库访问层中的应用:
cpp复制template<typename T>
class Field {
public:
Field(const char* name) : name_(name) {}
T getValue() const {
// 从数据库获取并转换类型
if constexpr (std::is_same_v<T, int>) {
return db_get_int(name_);
}
else if constexpr (std::is_same_v<T, std::string>) {
return db_get_string(name_);
}
// 其他类型处理...
}
private:
const char* name_;
};
// 使用示例
Field<int> id("user_id");
Field<std::string> name("user_name");
auto userId = id.getValue(); // 类型安全
这种模式在ORM库中广泛应用,既保持了接口的类型安全,又避免了虚函数调用的开销。
物理量纲检查是模板元编程的经典应用,能在编译期捕获单位不一致的错误,而无需任何运行时开销。
基本思路是将物理量的单位表示为模板参数:
cpp复制template<int M, int L, int T> // 质量、长度、时间的幂次
class Quantity {
public:
explicit Quantity(double val) : value(val) {}
double getValue() const { return value; }
private:
double value;
};
// 常用单位别名
using Mass = Quantity<1, 0, 0>; // kg
using Length = Quantity<0, 1, 0>; // m
using Time = Quantity<0, 0, 1>; // s
using Velocity = Quantity<0, 1, -1>; // m/s
using Acceleration = Quantity<0, 1, -2>; // m/s²
通过运算符重载实现类型安全的计算:
cpp复制// 加法:要求量纲相同
template<int M, int L, int T>
Quantity<M, L, T> operator+(Quantity<M, L, T> a, Quantity<M, L, T> b) {
return Quantity<M, L, T>(a.getValue() + b.getValue());
}
// 乘法:量纲指数相加
template<int M1, int L1, int T1, int M2, int L2, int T2>
Quantity<M1+M2, L1+L2, T1+T2> operator*(Quantity<M1, L1, T1> a,
Quantity<M2, L2, T2> b) {
return Quantity<M1+M2, L1+L2, T1+T2>(a.getValue() * b.getValue());
}
// 除法:量纲指数相减
template<int M1, int L1, int T1, int M2, int L2, int T2>
Quantity<M1-M2, L1-L2, T1-T2> operator/(Quantity<M1, L1, T1> a,
Quantity<M2, L2, T2> b) {
return Quantity<M1-M2, L1-L2, T1-T2>(a.getValue() / b.getValue());
}
cpp复制Length distance(100.0); // 100米
Time time(9.58); // 9.58秒
Velocity speed = distance / time; // 正确
Acceleration a = speed / time; // 正确
Mass m(80.0); // 80kg
auto wrong = distance + m; // 编译错误:量纲不匹配
auto wrong2 = speed * m; // 正确,得到动量(kg·m/s)
实际工程中可能需要更复杂的量纲系统:
扩展量纲种类:
cpp复制template<int M, int L, int T, int I, int Θ> // 增加电流和温度
class ExtendedQuantity;
单位系统转换:
cpp复制constexpr double MPH_TO_MPS = 0.44704;
using MilesPerHour = Quantity<0, 1, -1>;
using MetersPerSecond = Quantity<0, 1, -1>;
MetersPerSecond toMPS(MilesPerHour mph) {
return MetersPerSecond(mph.getValue() * MPH_TO_MPS);
}
编译时量纲检查:
cpp复制template<typename T>
constexpr bool is_velocity = false;
template<>
constexpr bool is_velocity<Velocity> = true;
static_assert(is_velocity<decltype(speed)>, "Must be velocity");
在航天器轨道计算等关键系统中,这类技术可以预防因单位混淆导致的严重错误,而且不会引入任何运行时开销。
嵌入式环境对代码有着特殊要求:确定性、小体积、低延迟。模板在这些场景中展现出独特价值。
静态分配容器:
cpp复制template<typename T, size_t Capacity>
class StaticVector {
public:
void push_back(const T& item) {
if (size_ >= Capacity)
throw std::out_of_range("Capacity exceeded");
new(&data_[size_++]) T(item);
}
// 其他接口...
private:
alignas(T) std::byte data_[Capacity * sizeof(T)];
size_t size_ = 0;
};
硬件寄存器映射:
cpp复制template<typename T, uintptr_t Address>
class Register {
public:
volatile T* operator->() {
return reinterpret_cast<volatile T*>(Address);
}
// 其他访问接口...
};
// 使用示例
Register<uint32_t, 0x40021000> RCC_CR;
RCC_CR->bits.HSEON = 1; // 直接操作硬件寄存器
编译时选择算法:
cpp复制template<unsigned Priority>
class TaskScheduler {
static_assert(Priority <= 3, "Only 4 priority levels supported");
void schedule() {
if constexpr (Priority == 0) {
// 最高优先级调度策略
} else if constexpr (Priority == 1) {
// 次高优先级策略
}
// ...
}
};
零开销抽象接口:
cpp复制template<typename Impl>
class SensorInterface : private Impl {
public:
auto read() { return static_cast<Impl*>(this)->read_impl(); }
// CRTP模式
};
class TemperatureSensor : public SensorInterface<TemperatureSensor> {
friend class SensorInterface<TemperatureSensor>;
float read_impl() { /* 实际实现 */ }
};
循环展开优化:
cpp复制template<size_t N>
struct Unroller {
template<typename F>
static void execute(F&& f) {
f(N-1);
Unroller<N-1>::execute(std::forward<F>(f));
}
};
template<>
struct Unroller<0> {
template<typename F>
static void execute(F&&) {}
};
// 使用示例
Unroller<8>::execute([](size_t i) {
// 循环体,会被完全展开
});
SIMD指令选择:
cpp复制template<typename T>
struct SIMDTraits;
template<>
struct SIMDTraits<float> {
using type = __m128;
static constexpr size_t width = 4;
};
template<typename T, size_t N>
class Vector {
using SIMD = SIMDTraits<T>;
// 使用SIMD指令优化实现...
};
在ARM Cortex-M4处理器上的实测数据显示,使用模板优化的数字信号处理算法比传统实现快2-3倍,同时代码体积减少约20%。
C++11/14/17/20引入的新特性极大增强了模板的能力和易用性。
类型安全的格式化输出:
cpp复制template<typename... Args>
void safe_printf(const char* fmt, Args... args) {
static_assert(((std::is_arithmetic_v<Args> ||
std::is_same_v<Args, const char*>) && ...),
"Only arithmetic types and strings allowed");
// 实际实现...
}
编译时接口检查:
cpp复制template<typename T, typename = void>
struct has_serialize : std::false_type {};
template<typename T>
struct has_serialize<T, std::void_t<decltype(std::declval<T>().serialize())>>
: std::true_type {};
template<typename T>
void saveData(const T& obj) {
if constexpr (has_serialize<T>::value) {
obj.serialize();
} else {
static_assert(false, "Type must have serialize method");
}
}
类型安全的SQL构建:
cpp复制template<size_t N>
struct FixedString {
char str[N]{};
constexpr FixedString(const char (&s)[N]) {
std::copy_n(s, N, str);
}
};
template<FixedString S>
class SQLQuery {
constexpr static auto query = S.str;
// 编译时验证SQL语法...
};
// 使用示例
using SafeQuery = SQLQuery<"SELECT * FROM users">;
C++20概念约束:
cpp复制template<typename T>
concept ThreadSafe = requires {
typename T::mutex_type;
{ T::lock() } -> std::same_as<void>;
{ T::unlock() } -> std::same_as<void>;
};
template<ThreadSafe T>
class GuardedResource {
// 实现...
};
编译时反射实验:
cpp复制template<typename T>
constexpr void print_fields() {
if constexpr (requires { typename std::meta::members_of<T>; }) {
constexpr auto members = std::meta::members_of<T>;
[&members]<size_t... I>(std::index_sequence<I...>) {
(std::cout << std::meta::name_of(members[I]) << '\n', ...);
}(std::make_index_sequence<members.size()>{});
}
}
这些新特性正在改变模板编程的面貌,使得原本复杂的模板代码变得更清晰、更易维护,同时保持了编译时计算的强大能力。
模板代码的调试和性能分析有其特殊性,需要专门的技术和工具支持。
模板相关的编译错误往往冗长难懂,可以采用以下策略:
分层诊断法:
静态断言先行:
cpp复制template<typename T>
void process(T value) {
static_assert(std::is_integral_v<T>, "Requires integral type");
// 其他实现...
}
类型打印技巧:
cpp复制template<typename T>
void debugType() {
struct Dummy;
static_assert(std::is_same_v<T, Dummy>, "Type is: ...");
// 编译器会显示T的实际类型
}
GCC的-ftemplate-backtrace-limit和Clang的-ftemplate-backtrace选项可以控制模板实例化跟踪的深度。例如:
bash复制g++ -ftemplate-backtrace-limit=5 ...
对于复杂项目,可以使用-fdump-class-hierarchy生成类层次结构图:
bash复制g++ -fdump-class-hierarchy -fdump-instantiation-dir=./inst ...
编译时间分析:
bash复制# Clang时间追踪
time -v clang++ -ftime-trace -c template_heavy.cpp
# GCC模板统计
g++ -Q --help=warning,-ftime-report
二进制大小分析:
bash复制nm --demangle --size-sort a.out | c++filt | grep -i template
运行时性能分析:
bash复制perf record ./template_app
perf annotate --source
分步实例化:
cpp复制template<typename T> struct Debug;
template<typename T>
void process(T value) {
Debug<T>{}; // 触发类型检查
// 实际处理...
}
编译时值输出:
cpp复制template<auto V>
struct DebugValue {
static_assert(V == 0, "Value is displayed here");
};
SFINAE调试:
cpp复制template<typename T, typename = void>
struct has_method : std::false_type {};
template<typename T>
struct has_method<T, std::void_t<decltype(std::declval<T>().method())>>
: std::true_type {};
static_assert(has_method<TestClass>::value, "Check failed");
在大型项目中,这些技术可以帮助定位95%以上的模板相关问题,显著提高开发效率。
模板不仅是实现泛型的工具,更是软件架构中的重要构建块。许多经典设计模式通过模板实现会有更好的类型安全性和性能表现。
传统策略模式依赖运行时多态,而模板策略在编译期确定:
cpp复制template<typename SortingStrategy>
class SortedContainer {
public:
void sort() {
SortingStrategy::sort(data_.begin(), data_.end());
}
private:
std::vector<int> data_;
};
struct QuickSort {
static void sort(auto begin, auto end) { /* 实现 */ }
};
struct MergeSort {
static void sort(auto begin, auto end) { /* 实现 */ }
};
// 使用示例
SortedContainer<QuickSort> quickSorted;
SortedContainer<MergeSort> mergeSorted;
结合类型标签的编译期工厂:
cpp复制struct WidgetA {};
struct WidgetB {};
template<typename T>
class WidgetFactory {
public:
auto create() {
if constexpr (std::is_same_v<T, WidgetA>) {
return createWidgetA();
} else if constexpr (std::is_same_v<T, WidgetB>) {
return createWidgetB();
}
}
private:
WidgetA createWidgetA() { /* 实现 */ }
WidgetB createWidgetB() { /* 实现 */ }
};
利用变参模板和std::visit实现:
cpp复制template<typename... Ts>
class Visitor {
public:
virtual ~Visitor() = default;
virtual void visit(Ts&...) = 0;
};
template<typename... Ts>
class Visitable {
public:
virtual void accept(Visitor<Ts...>& visitor) = 0;
};
// 具体实现示例
class Document : public Visitable<Paragraph, Image> {
void accept(Visitor<Paragraph, Image>& v) override {
v.visit(paragraphs, images);
}
private:
std::vector<Paragraph> paragraphs;
std::vector<Image> images;
};
现代C++库中常见的架构模式:
cpp复制template<typename T,
template<typename> class Allocator = std::allocator,
template<typename> class ThreadPolicy = SingleThreaded>
class AdvancedContainer {
// 实现...
};
// 使用示例
using SafeContainer = AdvancedContainer<int,
PoolAllocator,
MutexProtected>;
这种架构允许在多个正交维度上定制组件行为,同时保持类型安全和编译期优化机会。
理解C++模板与其他语言泛型系统的区别,有助于做出更合理的技术选型。
| 特性 | C++模板 | Java/C#泛型 |
|---|---|---|
| 类型擦除 | 无,生成具体类型代码 | 有,运行时只有一种类型 |
| 性能影响 | 零开销 | 装箱/拆箱可能影响性能 |
| 元编程能力 | 强大,图灵完备 | 有限,仅基本类型约束 |
| 代码生成时机 | 编译期 | 部分编译期,部分运行时 |
| 原生类型支持 | 直接支持 | 需要包装类 |
| 跨语言互操作 | 困难 | 相对容易 |
Rust的泛型系统与C++模板最为相似,但有更多安全保证:
rust复制// Rust泛型示例
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
关键差异点:
template<int N>)D语言的模板是对C++模板的改进和扩展:
d复制// D模板示例
template Matrix(T, int size) {
struct Matrix {
T[size][size] data;
// 实现...
}
}
alias Float3x3 = Matrix!(float, 3);
优势包括:
根据项目需求选择合适的技术:
在嵌入式系统、游戏引擎、高频交易等对性能要求极高的领域,C++模板仍然是无可替代的选择。
在实际工程中应用模板需要特别注意长期可维护性,避免过度设计带来的复杂性。
推荐的项目结构:
code复制include/
module/
public_interface.h
detail/
implementation.h
utilities.h
src/
module/
explicit_instantiations.cpp
tests/
module/
type_safety_checks.cpp
performance_benchmarks.cpp
关键原则:
良好的模板代码文档应包括:
类型要求:明确模板参数必须满足的条件
cpp复制/// @tparam T Must satisfy:
/// - std::regular (copyable, default constructible)
/// - operator< defined for total ordering
template<typename T>
class SortedCollection;
使用示例:展示典型和边界用例
cpp复制/// @example
/// SortedCollection<int> coll;
/// coll.insert(42);
/// assert(coll.contains(42));
性能保证:说明时间复杂度、异常安全等
cpp复制/// @complexity O(log n) insertion
/// @exception No-throw guarantee if T::operator< is no-throw
维护模板库的ABI兼容性需要特别注意:
避免修改已有模板的参数顺序
新增参数应带默认值
cpp复制// 原始版本
template<typename T>
class Widget;
// 扩展版本
template<typename T, typename Allocator = std::allocator<T>>
class Widget;
使用类型标签保持扩展性
cpp复制template<typename T, typename Policy = DefaultPolicy>
class AdvancedFeature;
审查模板代码时应特别关注:
在大型代码库中,建议建立模板使用统计机制,定期分析模板实例化情况,识别潜在的优化机会。
深入理解模板对程序性能的影响,掌握专业的优化技术。
斐波那契数列计算:
cpp复制template<unsigned N>
struct Fibonacci {
static constexpr unsigned value =
Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};
template<>
struct Fibonacci<0> { static constexpr unsigned value = 0; };
template<>
struct Fibonacci<1> { static constexpr unsigned value = 1; };
// 使用示例
constexpr auto fib10 = Fibonacci<10>::value; // 编译期计算
现代C++简化写法:
cpp复制constexpr unsigned fibonacci(unsigned n) {
return n <= 1 ? n : fibonacci(n-1) + fibonacci(n-2);
}
constexpr auto fib10 = fibonacci(10);
模板函数默认有内联倾向,但需谨慎使用:
cpp复制template<typename T>
inline void smallFunction(T param) { // 适合内联
// 简单操作
}
template<typename T>
void largeFunction(T param) { // 不适合内联
// 复杂逻辑
// 可能被多次实例化
}
优化建议:
__attribute__((noinline))或__declspec(noinline)显式控制使用工具分析模板实例化影响:
bash复制# GCC模板实例化统计
g++ -fdump-ipa-all -fdump-statistics -o /dev/null
# Clang模板分析
clang++ -Xclang -ast-print -fsyntax-only
典型优化前后的对比数据:
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 二进制大小 | 2.4MB | 1.7MB |
| 编译时间 | 42s | 28s |
| 模板实例化数量 | 1,248 | 587 |
利用模板实现数据布局优化:
cpp复制template<typename T, size_t N>
class SoA { // Structure of Arrays
public:
T& x(size_t i) { return x_[i]; }
T& y(size_t i) { return y_[i]; }
private:
std::array<T, N> x_;
std::array<T, N> y_;
};
// 使用示例
SoA<float, 1024> points;
// 连续访问x或y坐标,提高缓存命中率
在游戏引擎等性能敏感应用中,这类优化可以提升3-5倍的遍历速度。
模板虽然强大,但不当使用会引入各种安全隐患,需要特别注意。
数组退化问题:
cpp复制template<typename T>
void process(T* data, size_t size); // 安全版本
template<typename T, size_t N>
void process(T (&array)[N]); // 更安全的数组版本
// 危险用法
int data[10];
process(data, 10); // 可能越界
process(data); // 安全,N自动推导为10
指针与整数混淆:
cpp复制template<typename T>
void safeDelete(T*& ptr) {
static_assert(!std::is_integral_v<T>,
"Integers cannot be deleted");
delete ptr;
ptr = nullptr;
}
通用RAII包装器:
cpp复制template<typename T, typename Deleter = std::default_delete<T>>
class ScopedResource {
public:
explicit ScopedResource(T* res) : res_(res) {}
~ScopedResource() { Deleter()(res_); }
// 禁用拷贝
ScopedResource(const ScopedResource&) = delete;
ScopedResource& operator=(const ScopedResource&) = delete;
// 允许移动
ScopedResource(ScopedResource&& other) : res_(other.res_) {
other.res_ = nullptr;
}
T* get() const { return res_; }
private:
T* res_;
};
// 使用示例
ScopedResource<FILE, decltype([](FILE* f){ fclose(f); })> file(fopen("data.txt", "r"));
线程局部存储模板:
cpp复制template<typename T>
class ThreadLocal {
public:
T& get() {
static thread_local T instance;
return instance;
}
// 禁止拷贝
ThreadLocal() = default;
ThreadLocal(const ThreadLocal&) = delete;
ThreadLocal& operator=(const ThreadLocal&) = delete;
};
// 使用示例
ThreadLocal<std::mt19937> rng;
int value = std::uniform_int_distribution<>(0,100)(rng.get());
安全数值转换:
cpp复制template<typename To, typename From>
To safe_cast(From value) {
static_assert(std::is_arithmetic_v<From> && std::is_arithmetic_v<To>,
"Only arithmetic types supported");
if constexpr (std::is_signed_v<From> == std::is_signed_v<To>) {
// 同符号转换
if (value > std::numeric_limits<To>::max() ||
value < std::numeric_limits<To>::lowest()) {
throw std::overflow_error("Value out of range");
}
} else {
// 异符号转换需要额外检查
// 实现细节...
}
return static_cast<To>(value);
}
在金融、医疗等关键领域系统中,这些安全措施可以预防90%以上的类型相关运行时错误。
模板代码的测试有其特殊性,需要专门的策略和工具支持。
使用类型列表进行穷举测试:
cpp复制template<typename T>
class TestFixture : public ::testing::Test {};
using TestTypes = ::testing::Types<int, float, double, char>;
TYPED_TEST_SUITE(TestFixture, TestTypes);
TYPED_TEST(TestFixture, ExampleTest) {
TypeParam value{};
// 对每种类型执行测试
}
使用静态断言和SFINAE进行编译期测试:
cpp复制template<typename T>
constexpr bool has_serialize