嵌入式现代C++教程——模板特化与偏特化¶
在前面的章节中,我们学习了模板的基础语法和函数模板的推导规则。但有时候,通用的模板定义并不适合所有类型——你可能需要为某个特定类型提供完全不同的实现,或者为某类类型提供定制行为。
这就是模板特化发挥作用的地方。它是C++模板编程中最强大的工具之一,也是实现类型萃取(Type Traits)的基础。
本章我们将深入探讨:
- 全特化与偏特化的本质区别
- 类模板偏特化的匹配规则
- 为什么函数模板应该用重载而非特化
- 实战:实现一个完整的 traits 类
特化概述:为什么需要特化?¶
通用模板的局限性¶
假设我们正在编写一个通用的比较工具:
对于大多数类型,这工作得很好。但考虑这些场景:
// 场景1:C风格字符串
equals("hello", "hello"); // 比较指针地址,而非字符串内容!
// 场景2:浮点数
equals(0.1 + 0.2, 0.3); // 浮点精度问题,可能返回false
// 场景3:指针类型
equals<int*>(&p1, &p2); // 比较指针值,可能不是你想要的
这就是特化的用武之地——为特定类型提供专门的实现。
特化的分类¶
C++模板特化分为两大类:
| 特化类型 | 类模板 | 函数模板 |
|---|---|---|
| 全特化(Explicit Specialization) | 支持 | 支持(但不推荐) |
| 偏特化(Partial Specialization) | 支持 | 不支持 |
我们接下来详细探讨这两种特化。
全特化:完全定制¶
什么是全特化?¶
全特化是为特定模板参数提供完全独立的实现。特化后,原模板的所有代码都可以被替换。
类模板的全特化¶
// 主模板(通用版本)
template<typename T>
class TypeInfo {
public:
static const char* name() { return "unknown"; }
static constexpr size_t size = sizeof(T);
};
// 为int全特化
template<>
class TypeInfo<int> {
public:
static const char* name() { return "signed integer"; }
static constexpr size_t size = sizeof(int);
// 特化版本可以添加额外的成员
static constexpr bool is_signed = true;
static constexpr int min_value = INT_MIN;
static constexpr int max_value = INT_MAX;
};
// 为double全特化
template<>
class TypeInfo<double> {
public:
static const char* name() { return "double precision float"; }
static constexpr size_t size = sizeof(double);
static constexpr bool is_signed = true;
};
// 使用
std::cout << TypeInfo<float>::name(); // "unknown"(使用主模板)
std::cout << TypeInfo<int>::name(); // "signed integer"(使用特化)
std::cout << TypeInfo<double>::name(); // "double precision float"
语法要点:
template<>表示这是一个特化- 后面紧跟类名和具体类型:
class TypeInfo<int> - 特化版本可以完全重新定义类的内容
函数模板的全特化¶
// 主模板
template<typename T>
bool is_negative(T value) {
return value < 0;
}
// 为unsigned int全特化
template<>
bool is_negative<unsigned int>(unsigned int value) {
(void)value; // 避免unused参数警告
return false; // 无符号数永远不为负
}
// 使用
is_negative(-5); // 调用主模板,T=int
is_negative(5u); // 调用特化版本
is_negative(3.14); // 调用主模板,T=double
嵌入式应用场景:寄存器访问特化¶
// 通用的寄存器访问模板
template<typename T, uintptr_t Address>
class Register {
public:
static void write(T value) {
*reinterpret_cast<volatile T*>(Address) = value;
}
static T read() {
return *reinterpret_cast<volatile T*>(Address);
}
};
// 为布尔寄存器(1位)全特化
template<uintptr_t Address>
class Register<bool, Address> {
public:
static void write(bool value) {
if (value) {
*reinterpret_cast<volatile uint8_t*>(Address) = 1;
} else {
*reinterpret_cast<volatile uint8_t*>(Address) = 0;
}
}
static bool read() {
return *reinterpret_cast<volatile uint8_t*>(Address) != 0;
}
// 添加特有操作
static void set() {
*reinterpret_cast<volatile uint8_t*>(Address) = 1;
}
static void clear() {
*reinterpret_cast<volatile uint8_t*>(Address) = 0;
}
static void toggle() {
auto reg = reinterpret_cast<volatile uint8_t*>(Address);
*reg = !*reg;
}
};
// 使用
Register<uint32_t, 0x40021000> gpio_mode; // 通用版本
Register<bool, 0x40021010> gpio_output; // 特化版本,有set/clear/toggle
gpio_mode.write(0x44444444);
gpio_output.set();
偏特化:部分定制¶
什么是偏特化?¶
偏特化(部分特化)只对部分模板参数进行特化,或者对参数的某种属性进行特化。
关键点:
- 只有类模板支持偏特化
- 函数模板不支持偏特化
基于模板参数数量的偏特化¶
// 主模板:两个类型参数
template<typename T, typename U>
class Pair {
public:
T first;
U second;
Pair() : first{}, second{} {}
Pair(const T& t, const U& u) : first(t), second(u) {}
void print() const {
std::cout << "Pair<T, U>: " << first << ", " << second << '\n';
}
};
// 偏特化:两个类型相同时
template<typename T>
class Pair<T, T> {
public:
T first;
T second;
Pair() : first{}, second{} {}
Pair(const T& t, const T& u) : first(t), second(u) {}
// 可以添加针对相同类型的特殊操作
T sum() const { return first + second; }
bool are_equal() const { return first == second; }
void print() const {
std::cout << "Pair<T, T>: " << first << ", " << second << '\n';
}
};
// 偏特化:第二个参数是指针
template<typename T, typename U>
class Pair<T, U*> {
public:
T first;
U* second;
Pair() : first{}, second{nullptr} {}
Pair(const T& t, U* u) : first(t), second(u) {}
// 针对指针的特殊处理
void print() const {
std::cout << "Pair<T, U*>: " << first << ", "
<< (second ? *second : U{}) << '\n';
}
};
// 使用
Pair<int, double> p1; // 使用主模板
Pair<int, int> p2; // 使用偏特化<T, T>
Pair<int, double*> p3; // 使用偏特化<T, U*>
基于类型属性的偏特化¶
这是偏特化最强大的用法——针对类型的性质而非类型本身:
// 主模板
template<typename T>
class TypeProperties {
public:
static constexpr bool is_pointer = false;
static constexpr bool is_const = false;
static constexpr bool is_reference = false;
using value_type = T;
using base_type = T;
};
// 指针类型的偏特化
template<typename T>
class TypeProperties<T*> {
public:
static constexpr bool is_pointer = true;
static constexpr bool is_const = false;
static constexpr bool is_reference = false;
using value_type = T;
using base_type = T; // 去掉指针后的类型
};
// const指针的偏特化
template<typename T>
class TypeProperties<const T*> {
public:
static constexpr bool is_pointer = true;
static constexpr bool is_const = true;
static constexpr bool is_reference = false;
using value_type = T;
using base_type = T;
};
// 引用类型的偏特化
template<typename T>
class TypeProperties<T&> {
public:
static constexpr bool is_pointer = false;
static constexpr bool is_const = false;
static constexpr bool is_reference = true;
using value_type = T;
using base_type = T;
};
// 使用
TypeProperties<int>::is_pointer; // false
TypeProperties<int*>::is_pointer; // true
TypeProperties<const int*>::is_const; // true
TypeProperties<int&>::is_reference; // true
TypeProperties<int*>::base_type; // int
偏特化的层次结构¶
偏特化可以有多个层次,编译器会选择最特化的版本:
// 层次0:主模板
template<typename T>
class Container {
public:
static constexpr int level = 0;
};
// 层次1:指针偏特化
template<typename T>
class Container<T*> {
public:
static constexpr int level = 1;
};
// 层次2:const指针偏特化
template<typename T>
class Container<const T*> {
public:
static constexpr int level = 2;
};
// 层次3:指向const的const指针
template<typename T>
class Container<const T* const> {
public:
static constexpr int level = 3;
};
// 使用
Container<int>::level; // 0(主模板)
Container<int*>::level; // 1(指针)
Container<const int*>::level; // 2(const指针)
Container<const int* const>::level; // 3(const T* const)
多模板参数的偏特化匹配¶
当有多个偏特化版本时,编译器按照"最特化"原则选择:
// 主模板
template<typename T, typename U, int N>
class Test {
public:
static constexpr const char* desc = "primary";
};
// 偏特化1:固定第三个参数
template<typename T, typename U>
class Test<T, U, 10> {
public:
static constexpr const char* desc = "N=10";
};
// 偏特化2:前两个参数相同
template<typename T, int N>
class Test<T, T, N> {
public:
static constexpr const char* desc = "T=U";
};
// 偏特化3:前两个参数相同且N=10
template<typename T>
class Test<T, T, 10> {
public:
static constexpr const char* desc = "T=U, N=10";
};
// 使用
Test<int, double, 5>::desc; // "primary"(主模板)
Test<int, double, 10>::desc; // "N=10"(偏特化1)
Test<int, int, 5>::desc; // "T=U"(偏特化2)
Test<int, int, 10>::desc; // "T=U, N=10"(偏特化3,最特化)
编译器如何选择"最特化"版本?¶
偏特化选择遵循更特化(more specialized)规则:
| 规则 | 说明 |
|---|---|
| 部分参数固定 | 固定部分参数的比全参数的更特化 |
| 约束更多 | 约束更多的版本更特化 |
| 更具体的匹配 | 能匹配更多类型的版本更通用 |
// 示例:理解"更特化"
template<typename T>
struct A { static constexpr int value = 0; }; // 通用
template<typename T>
struct A<T*> { static constexpr int value = 1; }; // 更特化(指针)
template<typename T>
struct A<const T*> { static constexpr int value = 2; }; // 最特化(const指针)
A<int>::value; // 0
A<int*>::value; // 1
A<const int*>::value; // 2
匹配规则详解¶
完整的匹配顺序¶
当编译器遇到模板使用时,按以下顺序匹配:
匹配示例¶
// 主模板
template<typename T, typename U>
class Selector {
public:
static constexpr int choice = 0;
};
// 偏特化:T是指针
template<typename T, typename U>
class Selector<T*, U> {
public:
static constexpr int choice = 1;
};
// 偏特化:U是指针
template<typename T, typename U>
class Selector<T, U*> {
public:
static constexpr int choice = 2;
};
// 偏特化:两者都是指针
template<typename T, typename U>
class Selector<T*, U*> {
public:
static constexpr int choice = 3;
};
// 测试
static_assert(Selector<int, double>::choice == 0); // 主模板
static_assert(Selector<int*, double>::choice == 1); // T*偏特化
static_assert(Selector<int, double*>::choice == 2); // U*偏特化
static_assert(Selector<int*, double*>::choice == 3); // 都是指针
引用和cv限定符的匹配¶
// 主模板
template<typename T>
class Matcher {
public:
static constexpr int value = 0;
};
// const偏特化
template<typename T>
class Matcher<const T> {
public:
static constexpr int value = 1;
};
// 引用偏特化
template<typename T>
class Matcher<T&> {
public:
static constexpr int value = 2;
};
// rvalue引用偏特化
template<typename T>
class Matcher<T&&> {
public:
static constexpr int value = 3;
};
// const引用偏特化
template<typename T>
class Matcher<const T&> {
public:
static constexpr int value = 4;
};
// 测试
static_assert(Matcher<int>::value == 0); // 主模板
static_assert(Matcher<const int>::value == 1); // const偏特化
static_assert(Matcher<int&>::value == 2); // 引用偏特化
static_assert(Matcher<int&&>::value == 3); // rvalue引用
static_assert(Matcher<const int&>::value == 4); // const引用
注意事项:退化与匹配¶
类型在匹配时会考虑退化(decay):
// 主模板
template<typename T>
class DecayTest {
public:
static constexpr int value = 0;
static constexpr const char* name = "primary";
};
// 数组偏特化
template<typename T, size_t N>
class DecayTest<T[N]> {
public:
static constexpr int value = 1;
static constexpr const char* name = "array";
};
// 函数偏特化
template<typename T, typename... Args>
class DecayTest<T(Args...)> {
public:
static constexpr int value = 2;
static constexpr const char* name = "function";
};
// 指针偏特化
template<typename T>
class DecayTest<T*> {
public:
static constexpr int value = 3;
static constexpr const char* name = "pointer";
};
// 测试
DecayTest<int[10]>::name; // "array"
DecayTest<void(int)>::name; // "function"
DecayTest<int*>::name; // "pointer"
函数模板特化的真相¶
为什么函数模板"不支持"偏特化?¶
准确地说:C++允许函数模板的全特化,但不允许偏特化。当你想要"部分特化"函数模板时,应该使用函数重载。
函数模板全特化的语法¶
// 主模板
template<typename T>
void process(T value) {
std::cout << "Generic: " << value << '\n';
}
// 全特化:为int
template<>
void process<int>(int value) {
std::cout << "Int specialized: " << value << '\n';
}
// 全特化:可以省略<int>,编译器会推导
template<>
void process(double value) {
std::cout << "Double specialized: " << value << '\n';
}
// 使用
process(42); // 输出 "Int specialized: 42"
process(3.14); // 输出 "Double specialized: 3.14"
process('a'); // 输出 "Generic: a"
为什么不应该特化函数模板?¶
这是个很常见的陷阱。函数模板特化有几个严重问题:
问题1:特化不参与重载决议¶
// 主模板
template<typename T>
void func(T t) {
std::cout << "Template\n";
}
// 特化版本
template<>
void func<int>(int t) {
std::cout << "Specialization\n";
}
// 普通重载
void func(int t) {
std::cout << "Overload\n";
}
// 调用
func(42); // 输出 "Overload" —— 普通函数优先!
特化版本的优先级低于普通函数重载,这可能导致意想不到的行为。
问题2:无法偏特化¶
// ❌ 这不是合法的C++!
template<typename T>
void process(T* ptr) { // 这看起来像偏特化,实际上是新的主模板
std::cout << "Pointer\n";
}
// 这样做会创建一个新的主模板,导致二义性错误
template<typename T>
void process(T t) {
std::cout << "Generic\n";
}
process(42); // 错误!两个主模板都匹配
解决方案:使用函数重载¶
最佳实践:用函数重载替代函数模板特化。
// ✅ 正确方式:使用重载
// 主模板
template<typename T>
void func(T t) {
std::cout << "Generic: " << t << '\n';
}
// 重载:指针版本
template<typename T>
void func(T* ptr) {
std::cout << "Pointer: " << ptr << '\n';
}
// 重载:const char*版本
void func(const char* str) {
std::cout << "C-string: " << str << '\n';
}
// 使用
func(42); // "Generic: 42"
int x = 10;
func(&x); // "Pointer: <地址>"
func("hello"); // "C-string: hello"
特化与重载的决策表¶
| 场景 | 类模板 | 函数模板 |
|---|---|---|
| 需要完全定制 | 使用全特化 | 使用重载 |
| 需要部分定制 | 使用偏特化 | 使用重载 |
| 需要参与重载决议 | 不适用 | 使用重载 |
| 需要修改类成员 | 使用特化 | 不适用 |
何时使用函数模板特化?¶
函数模板特化应该只在以下有限场景使用:
-
修改类模板的成员函数:
-
与类模板特化配合:
除此之外,优先使用函数重载。
实战:实现完整的 Traits 类¶
Type Traits 是C++模板元编程的核心工具。让我们从头实现一个完整的 traits 类,包含指针类型的特化。
需求分析¶
我们的 traits 类需要提供:
- 类型信息:基础类型、值类型、去掉引用/const的类型
- 类型属性:是否为指针、引用、const、算术类型
- 类型转换:添加/移除指针、引用、const
- 类型关系:两个类型是否相同、一个是否可转换为另一个
基础框架¶
// 基础traits定义
template<typename T>
class TypeTraits {
public:
// 类型信息
using BaseType = T; // 去掉所有修饰符后的类型
using ValueType = T; // 容器存储的值类型
using ReferenceType = T&; // 对应的引用类型
using PointerType = T*; // 对应的指针类型
// 类型属性
static constexpr bool is_pointer = false;
static constexpr bool is_reference = false;
static constexpr bool is_const = false;
static constexpr bool is_volatile = false;
static constexpr bool is_void = false;
static constexpr bool is_integral = false;
static constexpr bool is_floating_point = false;
static constexpr bool is_arithmetic = false;
static constexpr bool is_signed = false;
static constexpr bool is_unsigned = false;
// 便捷常量
static constexpr bool is_primitive = is_arithmetic || is_void;
};
指针类型特化¶
// 指针类型的偏特化
template<typename T>
class TypeTraits<T*> {
public:
using BaseType = typename TypeTraits<T>::BaseType; // 递归获取
using ValueType = T; // 指针指向的类型
using ReferenceType = T&;
using PointerType = T*;
static constexpr bool is_pointer = true;
static constexpr bool is_reference = false;
static constexpr bool is_const = TypeTraits<T>::is_const;
static constexpr bool is_volatile = TypeTraits<T>::is_volatile;
static constexpr bool is_void = TypeTraits<T>::is_void;
static constexpr bool is_integral = TypeTraits<T>::is_integral;
static constexpr bool is_floating_point = TypeTraits<T>::is_floating_point;
static constexpr bool is_arithmetic = TypeTraits<T>::is_arithmetic;
static constexpr bool is_signed = TypeTraits<T>::is_signed;
static constexpr bool is_unsigned = TypeTraits<T>::is_unsigned;
static constexpr bool is_primitive = is_arithmetic || is_void;
};
// const指针的偏特化
template<typename T>
class TypeTraits<const T*> {
public:
using BaseType = typename TypeTraits<T>::BaseType;
using ValueType = const T;
using ReferenceType = const T&;
using PointerType = const T*;
static constexpr bool is_pointer = true;
static constexpr bool is_reference = false;
static constexpr bool is_const = true; // 这是const指针
static constexpr bool is_volatile = TypeTraits<T>::is_volatile;
static constexpr bool is_void = TypeTraits<T>::is_void;
static constexpr bool is_integral = TypeTraits<T>::is_integral;
static constexpr bool is_floating_point = TypeTraits<T>::is_floating_point;
static constexpr bool is_arithmetic = TypeTraits<T>::is_arithmetic;
static constexpr bool is_signed = TypeTraits<T>::is_signed;
static constexpr bool is_unsigned = TypeTraits<T>::is_unsigned;
static constexpr bool is_primitive = is_arithmetic || is_void;
};
引用类型特化¶
// 左值引用的偏特化
template<typename T>
class TypeTraits<T&> {
public:
using BaseType = typename TypeTraits<T>::BaseType;
using ValueType = T;
using ReferenceType = T&;
using PointerType = T*;
static constexpr bool is_pointer = false;
static constexpr bool is_reference = true;
static constexpr bool is_const = TypeTraits<T>::is_const;
static constexpr bool is_volatile = TypeTraits<T>::is_volatile;
static constexpr bool is_void = TypeTraits<T>::is_void;
static constexpr bool is_integral = TypeTraits<T>::is_integral;
static constexpr bool is_floating_point = TypeTraits<T>::is_floating_point;
static constexpr bool is_arithmetic = TypeTraits<T>::is_arithmetic;
static constexpr bool is_signed = TypeTraits<T>::is_signed;
static constexpr bool is_unsigned = TypeTraits<T>::is_unsigned;
static constexpr bool is_primitive = is_arithmetic || is_void;
};
// 右值引用的偏特化 (C++11)
template<typename T>
class TypeTraits<T&&> {
public:
using BaseType = typename TypeTraits<T>::BaseType;
using ValueType = T;
using ReferenceType = T&&;
using PointerType = T*;
static constexpr bool is_pointer = false;
static constexpr bool is_reference = true; // rvalue也是引用
static constexpr bool is_const = TypeTraits<T>::is_const;
static constexpr bool is_volatile = TypeTraits<T>::is_volatile;
static constexpr bool is_void = TypeTraits<T>::is_void;
static constexpr bool is_integral = TypeTraits<T>::is_integral;
static constexpr bool is_floating_point = TypeTraits<T>::is_floating_point;
static constexpr bool is_arithmetic = TypeTraits<T>::is_arithmetic;
static constexpr bool is_signed = TypeTraits<T>::is_signed;
static constexpr bool is_unsigned = TypeTraits<T>::is_unsigned;
static constexpr bool is_primitive = is_arithmetic || is_void;
};
基础类型的全特化¶
// void的特化
template<>
class TypeTraits<void> {
public:
using BaseType = void;
using ValueType = void;
using ReferenceType = void;
using PointerType = void*;
static constexpr bool is_pointer = false;
static constexpr bool is_reference = false;
static constexpr bool is_const = false;
static constexpr bool is_volatile = false;
static constexpr bool is_void = true;
static constexpr bool is_integral = false;
static constexpr bool is_floating_point = false;
static constexpr bool is_arithmetic = false;
static constexpr bool is_signed = false;
static constexpr bool is_unsigned = false;
static constexpr bool is_primitive = true;
};
// 整数类型的宏辅助
#define INTEGRAL_TRAITS(Type, IsSigned) \
template<> \
class TypeTraits<Type> { \
public: \
using BaseType = Type; \
using ValueType = Type; \
using ReferenceType = Type&; \
using PointerType = Type*; \
\
static constexpr bool is_pointer = false; \
static constexpr bool is_reference = false; \
static constexpr bool is_const = false; \
static constexpr bool is_volatile = false; \
static constexpr bool is_void = false; \
static constexpr bool is_integral = true; \
static constexpr bool is_floating_point = false; \
static constexpr bool is_arithmetic = true; \
static constexpr bool is_signed = IsSigned; \
static constexpr bool is_unsigned = !IsSigned; \
\
static constexpr bool is_primitive = true; \
}
// 为所有整数类型定义特化
INTEGRAL_TRAITS(char, true);
INTEGRAL_TRAITS(signed char, true);
INTEGRAL_TRAITS(unsigned char, false);
INTEGRAL_TRAITS(short, true);
INTEGRAL_TRAITS(unsigned short, false);
INTEGRAL_TRAITS(int, true);
INTEGRAL_TRAITS(unsigned int, false);
INTEGRAL_TRAITS(long, true);
INTEGRAL_TRAITS(unsigned long, false);
INTEGRAL_TRAITS(long long, true);
INTEGRAL_TRAITS(unsigned long long, false);
// 浮点类型的宏辅助
#define FLOATING_TRAITS(Type) \
template<> \
class TypeTraits<Type> { \
public: \
using BaseType = Type; \
using ValueType = Type; \
using ReferenceType = Type&; \
using PointerType = Type*; \
\
static constexpr bool is_pointer = false; \
static constexpr bool is_reference = false; \
static constexpr bool is_const = false; \
static constexpr bool is_volatile = false; \
static constexpr bool is_void = false; \
static constexpr bool is_integral = false; \
static constexpr bool is_floating_point = true; \
static constexpr bool is_arithmetic = true; \
static constexpr bool is_signed = true; \
static constexpr bool is_unsigned = false; \
\
static constexpr bool is_primitive = true; \
}
FLOATING_TRAITS(float);
FLOATING_TRAITS(double);
FLOATING_TRAITS(long double);
辅助别名模板¶
// 便捷的类型别名
template<typename T>
using BaseType_t = typename TypeTraits<T>::BaseType;
template<typename T>
using ValueType_t = typename TypeTraits<T>::ValueType;
template<typename T>
using AddPointer_t = typename TypeTraits<T>::PointerType;
template<typename T>
using AddReference_t = typename TypeTraits<T>::ReferenceType;
// 检查类型属性的常量
template<typename T>
inline constexpr bool is_pointer_v = TypeTraits<T>::is_pointer;
template<typename T>
inline constexpr bool is_reference_v = TypeTraits<T>::is_reference;
template<typename T>
inline constexpr bool is_arithmetic_v = TypeTraits<T>::is_arithmetic;
使用示例¶
// 测试代码
void test_type_traits() {
// 指针类型
static_assert(is_pointer_v<int*> == true);
static_assert(is_pointer_v<int> == false);
static_assert(is_pointer_v<const int*> == true);
// 引用类型
static_assert(is_reference_v<int&> == true);
static_assert(is_reference_v<int> == false);
static_assert(is_reference_v<int&&> == true);
// 算术类型
static_assert(is_arithmetic_v<int> == true);
static_assert(is_arithmetic_v<float> == true);
static_assert(is_arithmetic_v<int*> == false);
// 嵌套类型
static_assert(std::is_same_v<ValueType_t<int*>, int>);
static_assert(std::is_same_v<BaseType_t<const int&>, int>);
static_assert(std::is_same_v<BaseType_t<int***>, int>);
}
嵌入式应用:智能指针 traits¶
// 用于嵌入式智能指针的traits
template<typename T>
class SmartPtrTraits {
public:
using pointer_type = T*;
using element_type = T;
using deleter_type = void(*)(T*);
static void default_delete(T* ptr) {
delete ptr;
}
static constexpr bool is_array = false;
static constexpr bool is_shared = false;
};
// 数组类型的偏特化
template<typename T>
class SmartPtrTraits<T[]> {
public:
using pointer_type = T*;
using element_type = T;
using deleter_type = void(*)(T*);
static void default_delete(T* ptr) {
delete[] ptr;
}
static constexpr bool is_array = true;
static constexpr bool is_shared = false;
};
// 使用traits的智能指针基类
template<typename T, typename Traits = SmartPtrTraits<T>>
class SmartPtrBase {
protected:
using Ptr = typename Traits::pointer_type;
Ptr ptr_;
void cleanup() {
if (ptr_) {
Traits::default_delete(ptr_);
}
}
public:
constexpr SmartPtrBase(std::nullptr_t = nullptr) noexcept : ptr_(nullptr) {}
explicit SmartPtrBase(Ptr p) noexcept : ptr_(p) {}
~SmartPtrBase() {
cleanup();
}
// 禁用拷贝
SmartPtrBase(const SmartPtrBase&) = delete;
SmartPtrBase& operator=(const SmartPtrBase&) = delete;
// 支持移动
SmartPtrBase(SmartPtrBase&& other) noexcept : ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
SmartPtrBase& operator=(SmartPtrBase&& other) noexcept {
if (this != &other) {
cleanup();
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
return *this;
}
// 访问器
Ptr get() const noexcept { return ptr_; }
explicit operator bool() const noexcept { return ptr_ != nullptr; }
// 特化操作
void reset(Ptr p = nullptr) noexcept {
cleanup();
ptr_ = p;
}
Ptr release() noexcept {
Ptr tmp = ptr_;
ptr_ = nullptr;
return tmp;
}
};
// 具体的智能指针实现
template<typename T>
class UniquePtr : public SmartPtrBase<T, SmartPtrTraits<T>> {
using Base = SmartPtrBase<T, SmartPtrTraits<T>>;
public:
using Base::Base;
// 解引用操作符
T& operator*() const { return *this->get(); }
T* operator->() const { return this->get(); }
};
// 数组特化版本
template<typename T>
class UniquePtr<T[]> : public SmartPtrBase<T[], SmartPtrTraits<T[]>> {
using Base = SmartPtrBase<T[], SmartPtrTraits<T[]>>;
public:
using Base::Base;
// 数组下标操作
T& operator[](size_t index) const { return this->get()[index]; }
};
// 使用
UniquePtr<int> ptr1(new int(42));
UniquePtr<int[]> arr(new int[10]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
完整 TypeTraits 实现(可展开)
#ifndef TYPE_TRAITS_HPP
#define TYPE_TRAITS_HPP
#include <cstddef>
#include <cstdint>
template<typename T>
class TypeTraits {
public:
using BaseType = T;
using ValueType = T;
using ReferenceType = T&;
using PointerType = T*;
static constexpr bool is_pointer = false;
static constexpr bool is_reference = false;
static constexpr bool is_const = false;
static constexpr bool is_volatile = false;
static constexpr bool is_void = false;
static constexpr bool is_integral = false;
static constexpr bool is_floating_point = false;
static constexpr bool is_arithmetic = false;
static constexpr bool is_signed = false;
static constexpr bool is_unsigned = false;
static constexpr bool is_primitive = is_arithmetic || is_void;
};
// 指针偏特化
template<typename T>
class TypeTraits<T*> {
public:
using BaseType = typename TypeTraits<T>::BaseType;
using ValueType = T;
using ReferenceType = T&;
using PointerType = T*;
static constexpr bool is_pointer = true;
static constexpr bool is_const = TypeTraits<T>::is_const;
static constexpr bool is_volatile = TypeTraits<T>::is_volatile;
static constexpr bool is_void = TypeTraits<T>::is_void;
static constexpr bool is_integral = TypeTraits<T>::is_integral;
static constexpr bool is_floating_point = TypeTraits<T>::is_floating_point;
static constexpr bool is_arithmetic = TypeTraits<T>::is_arithmetic;
static constexpr bool is_signed = TypeTraits<T>::is_signed;
static constexpr bool is_unsigned = TypeTraits<T>::is_unsigned;
static constexpr bool is_primitive = is_arithmetic || is_void;
};
// const指针偏特化
template<typename T>
class TypeTraits<const T*> {
public:
using BaseType = typename TypeTraits<T>::BaseType;
using ValueType = const T;
using ReferenceType = const T&;
using PointerType = const T*;
static constexpr bool is_pointer = true;
static constexpr bool is_const = true;
static constexpr bool is_volatile = TypeTraits<T>::is_volatile;
static constexpr bool is_void = TypeTraits<T>::is_void;
static constexpr bool is_integral = TypeTraits<T>::is_integral;
static constexpr bool is_floating_point = TypeTraits<T>::is_floating_point;
static constexpr bool is_arithmetic = TypeTraits<T>::is_arithmetic;
static constexpr bool is_signed = TypeTraits<T>::is_signed;
static constexpr bool is_unsigned = TypeTraits<T>::is_unsigned;
static constexpr bool is_primitive = is_arithmetic || is_void;
};
// 引用偏特化
template<typename T>
class TypeTraits<T&> {
public:
using BaseType = typename TypeTraits<T>::BaseType;
using ValueType = T;
using ReferenceType = T&;
using PointerType = T*;
static constexpr bool is_reference = true;
static constexpr bool is_const = TypeTraits<T>::is_const;
static constexpr bool is_volatile = TypeTraits<T>::is_volatile;
static constexpr bool is_void = TypeTraits<T>::is_void;
static constexpr bool is_integral = TypeTraits<T>::is_integral;
static constexpr bool is_floating_point = TypeTraits<T>::is_floating_point;
static constexpr bool is_arithmetic = TypeTraits<T>::is_arithmetic;
static constexpr bool is_signed = TypeTraits<T>::is_signed;
static constexpr bool is_unsigned = TypeTraits<T>::is_unsigned;
static constexpr bool is_primitive = is_arithmetic || is_void;
};
// rvalue引用偏特化
template<typename T>
class TypeTraits<T&&> {
public:
using BaseType = typename TypeTraits<T>::BaseType;
using ValueType = T;
using ReferenceType = T&&;
using PointerType = T*;
static constexpr bool is_reference = true;
static constexpr bool is_const = TypeTraits<T>::is_const;
static constexpr bool is_volatile = TypeTraits<T>::is_volatile;
static constexpr bool is_void = TypeTraits<T>::is_void;
static constexpr bool is_integral = TypeTraits<T>::is_integral;
static constexpr bool is_floating_point = TypeTraits<T>::is_floating_point;
static constexpr bool is_arithmetic = TypeTraits<T>::is_arithmetic;
static constexpr bool is_signed = TypeTraits<T>::is_signed;
static constexpr bool is_unsigned = TypeTraits<T>::is_unsigned;
static constexpr bool is_primitive = is_arithmetic || is_void;
};
// void特化
template<>
class TypeTraits<void> {
public:
using BaseType = void;
using ValueType = void;
using ReferenceType = void;
using PointerType = void*;
static constexpr bool is_pointer = false;
static constexpr bool is_reference = false;
static constexpr bool is_const = false;
static constexpr bool is_volatile = false;
static constexpr bool is_void = true;
static constexpr bool is_integral = false;
static constexpr bool is_floating_point = false;
static constexpr bool is_arithmetic = false;
static constexpr bool is_signed = false;
static constexpr bool is_unsigned = false;
static constexpr bool is_primitive = true;
};
// 整数类型宏
#define INTEGRAL_TRAITS(Type, IsSigned) \
template<> \
class TypeTraits<Type> { \
public: \
using BaseType = Type; \
using ValueType = Type; \
using ReferenceType = Type&; \
using PointerType = Type*; \
\
static constexpr bool is_pointer = false; \
static constexpr bool is_reference = false; \
static constexpr bool is_const = false; \
static constexpr bool is_volatile = false; \
static constexpr bool is_void = false; \
static constexpr bool is_integral = true; \
static constexpr bool is_floating_point = false; \
static constexpr bool is_arithmetic = true; \
static constexpr bool is_signed = IsSigned; \
static constexpr bool is_unsigned = !IsSigned; \
static constexpr bool is_primitive = true; \
}
INTEGRAL_TRAITS(char, true);
INTEGRAL_TRAITS(signed char, true);
INTEGRAL_TRAITS(unsigned char, false);
INTEGRAL_TRAITS(short, true);
INTEGRAL_TRAITS(unsigned short, false);
INTEGRAL_TRAITS(int, true);
INTEGRAL_TRAITS(unsigned int, false);
INTEGRAL_TRAITS(long, true);
INTEGRAL_TRAITS(unsigned long, false);
INTEGRAL_TRAITS(long long, true);
INTEGRAL_TRAITS(unsigned long long, false);
INTEGRAL_TRAITS(int8_t, true);
INTEGRAL_TRAITS(int16_t, true);
INTEGRAL_TRAITS(int32_t, true);
INTEGRAL_TRAITS(int64_t, true);
INTEGRAL_TRAITS(uint8_t, false);
INTEGRAL_TRAITS(uint16_t, false);
INTEGRAL_TRAITS(uint32_t, false);
INTEGRAL_TRAITS(uint64_t, false);
// 浮点类型宏
#define FLOATING_TRAITS(Type) \
template<> \
class TypeTraits<Type> { \
public: \
using BaseType = Type; \
using ValueType = Type; \
using ReferenceType = Type&; \
using PointerType = Type*; \
\
static constexpr bool is_pointer = false; \
static constexpr bool is_reference = false; \
static constexpr bool is_const = false; \
static constexpr bool is_volatile = false; \
static constexpr bool is_void = false; \
static constexpr bool is_integral = false; \
static constexpr bool is_floating_point = true; \
static constexpr bool is_arithmetic = true; \
static constexpr bool is_signed = true; \
static constexpr bool is_unsigned = false; \
static constexpr bool is_primitive = true; \
}
FLOATING_TRAITS(float);
FLOATING_TRAITS(double);
FLOATING_TRAITS(long double);
// 便捷别名和变量模板
template<typename T>
using BaseType_t = typename TypeTraits<T>::BaseType;
template<typename T>
using ValueType_t = typename TypeTraits<T>::ValueType;
template<typename T>
using AddPointer_t = typename TypeTraits<T>::PointerType;
template<typename T>
using AddReference_t = typename TypeTraits<T>::ReferenceType;
template<typename T>
inline constexpr bool is_pointer_v = TypeTraits<T>::is_pointer;
template<typename T>
inline constexpr bool is_reference_v = TypeTraits<T>::is_reference;
template<typename T>
inline constexpr bool is_arithmetic_v = TypeTraits<T>::is_arithmetic;
template<typename T>
inline constexpr bool is_integral_v = TypeTraits<T>::is_integral;
template<typename T>
inline constexpr bool is_floating_point_v = TypeTraits<T>::is_floating_point;
#endif // TYPE_TRAITS_HPP
```
</details>
------
## 标准库溯源:std::iterator_traits
### 设计背景
在C++标准库中,`std::iterator_traits` 是特化的经典应用案例。它允许算法以统一方式处理不同类型的迭代器。
### iterator_traits 的结构
```cpp
// C++标准库中的简化版本
namespace std {
template<typename Iterator>
class iterator_traits {
public:
using iterator_category = typename Iterator::iterator_category;
using value_type = typename Iterator::value_type;
using difference_type = typename Iterator::difference_type;
using pointer = typename Iterator::pointer;
using reference = typename Iterator::reference;
};
// 指针类型的偏特化!
template<typename T>
class iterator_traits<T*> {
public:
using iterator_category = random_access_iterator_tag;
using value_type = T;
using difference_type = ptrdiff_t;
using pointer = T*;
using reference = T&;
};
// const指针的偏特化
template<typename T>
class iterator_traits<const T*> {
public:
using iterator_category = random_access_iterator_tag;
using value_type = T; // 注意:是T而非const T
using difference_type = ptrdiff_t;
using pointer = const T*;
using reference = const T&;
};
} // namespace std
template<typename T>
class MyIterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
// ... 迭代器实现
};
template<typename Iter>
void process(Iter begin, Iter end) {
// 通过traits获取类型
using traits = std::iterator_traits<Iter>;
using value_type = typename traits::value_type;
for (auto it = begin; it != end; ++it) {
value_type temp = *it; // 知道如何存储值
// 处理...
}
}
// 对于自定义迭代器
MyIterator<int> it1, it2;
process(it1, it2); // 使用主模板,从迭代器类型中提取
// 对于原生指针
int arr[10];
process(arr, arr + 10); // 使用指针偏特化!
#include <iterator>
template<typename T, size_t Size>
class RingBuffer {
public:
// 迭代器定义
class iterator {
public:
// 标准迭代器类型定义
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
iterator(RingBuffer* buf, size_t pos)
: buffer_(buf), pos_(pos) {}
// 解引用
reference operator*() const {
return buffer_->data_[pos_ % Size];
}
pointer operator->() const {
return &buffer_->data_[pos_ % Size];
}
// 算术运算
iterator& operator++() {
++pos_;
return *this;
}
iterator operator++(int) {
iterator tmp = *this;
++pos_;
return tmp;
}
iterator& operator--() {
--pos_;
return *this;
}
iterator operator--(int) {
iterator tmp = *this;
--pos_;
return tmp;
}
iterator operator+(difference_type n) const {
return iterator(buffer_, pos_ + n);
}
difference_type operator-(const iterator& other) const {
return pos_ - other.pos_;
}
// 比较
bool operator==(const iterator& other) const {
return buffer_ == other.buffer_ && pos_ == other.pos_;
}
bool operator!=(const iterator& other) const {
return !(*this == other);
}
private:
RingBuffer* buffer_;
size_t pos_;
};
// 构造函数
RingBuffer() : head_(0), tail_(0), full_(false) {}
// 元素访问
bool push(const T& item) {
if (full_) {
return false; // 缓冲区满
}
data_[tail_] = item;
tail_ = (tail_ + 1) % Size;
full_ = (head_ == tail_);
return true;
}
bool pop(T& item) {
if (empty()) {
return false; // 缓冲区空
}
item = data_[head_];
head_ = (head_ + 1) % Size;
full_ = false;
return true;
}
// 迭代器接口
iterator begin() { return iterator(this, head_); }
iterator end() {
return iterator(this, full_ ? tail_ + Size : tail_);
}
bool empty() const {
return !full_ && (head_ == tail_);
}
bool full() const {
return full_;
}
private:
T data_[Size];
size_t head_; // 读位置
size_t tail_; // 写位置
bool full_;
};
// 使用示例
void test_ring_buffer() {
RingBuffer<int, 8> buffer;
// 添加元素
for (int i = 0; i < 5; ++i) {
buffer.push(i);
}
// 使用迭代器遍历
auto it = buffer.begin();
auto end = buffer.end();
// 使用iterator_traits获取类型
using traits = std::iterator_traits<decltype(it)>;
using value_type = typename traits::value_type;
static_assert(std::is_same_v<value_type, int>);
// 算法兼容:可以使用STL算法
value_type sum = 0;
for (; it != end; ++it) {
sum += *it;
}
// 或使用标准算法
RingBuffer<int, 8> buffer2;
for (int i = 0; i < 5; ++i) buffer2.push(i * 2);
// std::copy兼容!
std::copy(buffer2.begin(), buffer2.end(),
std::ostream_iterator<int>(std::cout, " "));
}
// C++20风格
template<typename Iter>
using iter_value_t = typename std::iterator_traits<Iter>::value_type;
template<typename Iter>
using iter_reference_t = typename std::iterator_traits<Iter>::reference;
// 使用
template<std::input_iterator Iter>
void algorithm(Iter begin, Iter end) {
std::iter_value_t<Iter> sum{}; // 更简洁的语法
}
// ❌ 错误:无限递归
template<typename T>
struct Traits {
using type = typename Traits<T*>::type; // 永远不会到达基础情况
};
// ✅ 正确:提供基础情况
template<typename T>
struct Traits {
using type = T;
};
template<typename T>
struct Traits<T*> {
using type = typename Traits<T>::type; // 递归但会终止
};
// ❌ 不要依赖特化的定义顺序
template<typename T>
struct A;
template<typename T>
struct A<T*> {
using type = T;
};
template<typename T>
struct A {
using type = T;
};
// ✅ 主模板总是先定义
template<typename T>
struct A {
using type = T;
};
template<typename T>
struct A<T*> {
using type = typename A<T>::type; // 可以引用主模板
};
// ❌ 错误:const T& 不能正确匹配
template<typename T>
struct RemoveConst {
using type = T;
};
template<typename T>
struct RemoveConst<const T> {
using type = T;
};
RemoveConst<const int&>::type; // 还是 const int&,没去掉const!
// ✅ 正确:先去掉引用,再处理const
template<typename T>
struct RemoveConstImpl {
using type = T;
};
template<typename T>
struct RemoveConstImpl<const T> {
using type = T;
};
template<typename T>
struct RemoveConst {
using type = typename RemoveConstImpl<std::remove_reference_t<T>>::type;
};
RemoveConst<const int&>::type; // int,正确!
// ❌ 错误:想特化但实际上定义了新模板
template<typename T>
void f(T*);
template<typename T>
void f(T); // 二义性!
f(42); // 哪个f?
// ✅ 正确:使用重载
template<typename T>
void f(T);
template<typename T>
void f(T*); // 重载,不是特化
template<typename T>
struct Traits {
using value_type = T;
};
template<typename T>
void func() {
// ❌ 错误:编译器不知道 Traits<T>::value_type 是类型
Traits<T>::value_type x = 10;
// ✅ 正确:使用 typename
typename Traits<T>::value_type x = 10;
}
// ❌ 可能有问题:enable_if作为偏特化参数
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
struct OnlyIntegral {
static constexpr bool value = true;
};
// 编译错误,而不是替换失败
OnlyIntegral<double>::value;
// ✅ 使用 SFINAE 或 Concepts (C++20)
template<typename T, typename = void>
struct OnlyIntegral {
static constexpr bool value = false;
};
template<typename T>
struct OnlyIntegral<T, std::enable_if_t<std::is_integral_v<T>>> {
static constexpr bool value = true;
};
// 或 C++20
template<typename T>
concept Integral = std::is_integral_v<T>;
template<Integral T>
struct OnlyIntegral {
static constexpr bool value = true;
};
// 传统方式:使用SFINAE和特化
template<typename T, typename = void>
struct SupportsIncrement {
static constexpr bool value = false;
};
template<typename T>
struct SupportsIncrement<T,
std::void_t<decltype(++std::declval<T&>())>> {
static constexpr bool value = true;
};
// C++20:使用concept
template<typename T>
concept SupportsIncrement = requires(T t) {
{ ++t } -> std::same_as<T&>;
};
// 使用
template<SupportsIncrement T>
void increment_all(T* begin, T* end) {
for (auto it = begin; it != end; ++it) {
++(*it);
}
}
// 传统:偏特化
template<typename T>
class SmartPtr {
// 通用实现
};
template<typename T>
class SmartPtr<T[]> {
// 数组特化
};
// C++20:使用 requires
template<typename T>
class SmartPtr {
// 通用实现
};
template<typename T>
requires std::is_array_v<T>
class SmartPtr<T> {
// 数组版本(实际上是偏特化的语法糖)
};
// 传统:函数重载 + SFINAE
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
process(T value) {
return value + 1;
}
template<typename T>
std::enable_if_t<std::is_floating_point_v<T>, T>
process(T value) {
return value * 2.0;
}
// C++20:concept 重载
template<std::integral T>
T process(T value) {
return value + 1;
}
template<std::floating_point T>
T process(T value) {
return value * 2.0;
}