类型萃取 - 从零开始教学
什么是类型萃取?
类型萃取(Type Traits)是 C++ 模板元编程的核心技术,用于在编译期获取类型的信息或转换类型。
第一章:最基础的类型萃取
1.1 判断类型是否相同
// 最基础版本 - C++03 风格
template<typename T, typename U>
struct is_same {
static const bool value = false;
};
// 特化版本:当两个类型相同时
template<typename T>
struct is_same<T, T> {
static const bool value = true;
};
// 使用示例
#include <iostream>
int main() {
std::cout << is_same<int, int>::value << std::endl; // 输出:1 (true)
std::cout << is_same<int, double>::value << std::endl; // 输出:0 (false)
}
1.2 移除引用
// 主模板:处理非引用类型
template<typename T>
struct remove_reference {
typedef T type;
};
// 特化版本:处理左值引用
template<typename T>
struct remove_reference<T&> {
typedef T type;
};
// 特化版本:处理右值引用(C++11)
template<typename T>
struct remove_reference<T&&> {
typedef T type;
};
// 使用示例
#include <iostream>
int main() {
remove_reference<int>::type a; // int
remove_reference<int&>::type b; // int
remove_reference<int&&>::type c; // int
}
第二章:条件类型选择
2.1 条件类型选择器
// 条件选择模板
template<bool Condition, typename TrueType, typename FalseType>
struct conditional {
typedef FalseType type;
};
// 特化版本:当条件为true时
template<typename TrueType, typename FalseType>
struct conditional<true, TrueType, FalseType> {
typedef TrueType type;
};
// 使用示例
#include <iostream>
template<typename T>
void print_type() {
typedef typename conditional<sizeof(T) > 4, long, short>::type SelectType;
std::cout << "Selected type size: " << sizeof(SelectType) << std::endl;
}
int main() {
print_type<double>(); // 如果 sizeof(double) > 4,选择 long
print_type<char>(); // 如果 sizeof(char) > 4,选择 short
}
第三章:检测成员是否存在(SFINAE)
3.1 检测类型是否有某个成员
// C++03 版本:检测是否有 pointer 成员
template<typename T>
struct has_pointer {
private:
// 尝试获取 pointer 类型,如果失败则选择这个函数
template<typename U>
static char test(...);
// 如果能成功获取 pointer 类型,则选择这个函数
template<typename U>
static int test(typename U::pointer* = 0);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(int);
};
// 更现代的 C++11 版本
template<typename, typename = void>
struct has_pointer : std::false_type {};
template<typename T>
struct has_pointer<T, std::void_t<typename T::pointer>> : std::true_type {};
3.2 实际应用:__pointer 的实现
// 主模板:如果 T 有 pointer 成员,使用它
template<typename T, bool HasPointer = has_pointer<T>::value>
struct get_pointer {
typedef typename T::pointer type;
};
// 特化版本:如果 T 没有 pointer 成员,使用默认指针
template<typename T>
struct get_pointer<T, false> {
typedef T* type;
};
// 使用示例
struct MyAllocator {
typedef int* pointer; // 自定义指针类型
};
struct DefaultAllocator {
// 没有 pointer 成员
};
int main() {
get_pointer<MyAllocator>::type p1; // int*
get_pointer<DefaultAllocator>::type p2; // DefaultAllocator*
}
第四章:实际项目中的应用
4.1 自定义智能指针的指针类型选择
// 智能指针模板
template<typename T, typename Deleter = std::default_delete<T>>
class my_unique_ptr {
private:
// 使用类型萃取选择指针类型
typedef typename get_pointer<Deleter>::type pointer;
pointer ptr_;
Deleter deleter_;
public:
explicit my_unique_ptr(pointer p = nullptr) : ptr_(p) {}
~my_unique_ptr() {
if (ptr_) {
deleter_(ptr_);
}
}
pointer get() const { return ptr_; }
// ... 其他成员函数
};
4.2 容器中的类型萃取
// 简化的 vector 实现
template<typename T, typename Allocator = std::allocator<T>>
class my_vector {
private:
// 萃取各种类型
typedef typename Allocator::value_type value_type;
typedef typename get_pointer<Allocator>::type pointer;
typedef typename get_pointer<Allocator>::type const_pointer;
pointer data_;
size_t size_;
size_t capacity_;
public:
// ... 构造函数、析构函数等
pointer data() { return data_; }
const_pointer data() const { return data_; }
};
第五章:从零实现完整的类型萃取库
5.1 基础类型判断
// 类型分类
template<typename T>
struct type_traits {
static const bool is_pointer = false;
static const bool is_reference = false;
static const bool is_const = false;
static const bool is_void = false;
};
// 特化版本
template<typename T>
struct type_traits<T*> {
static const bool is_pointer = true;
static const bool is_reference = false;
static const bool is_const = false;
static const bool is_void = false;
};
template<typename T>
struct type_traits<T&> {
static const bool is_pointer = false;
static const bool is_reference = true;
static const bool is_const = false;
static const bool is_void = false;
};
template<typename T>
struct type_traits<const T> {
static const bool is_pointer = type_traits<T>::is_pointer;
static const bool is_reference = type_traits<T>::is_reference;
static const bool is_const = true;
static const bool is_void = type_traits<T>::is_void;
};
template<>
struct type_traits<void> {
static const bool is_pointer = false;
static const bool is_reference = false;
static const bool is_const = false;
static const bool is_void = true;
};
5.2 类型转换
// 添加 const
template<typename T>
struct add_const {
typedef const T type;
};
// 移除 const
template<typename T>
struct remove_const {
typedef T type;
};
template<typename T>
struct remove_const<const T> {
typedef T type;
};
// 添加指针
template<typename T>
struct add_pointer {
typedef T* type;
};
// 移除指针
template<typename T>
struct remove_pointer {
typedef T type;
};
template<typename T>
struct remove_pointer<T*> {
typedef T type;
};
template<typename T>
struct remove_pointer<T* const> {
typedef T type;
};
第六章:现代 C++ 中的简化
6.1 C++11 简化版本
// 使用别名模板简化
template<typename T>
using remove_reference_t = typename remove_reference<T>::type;
template<typename T>
using conditional_t = typename conditional<B, T, F>::type;
template<bool B, typename T, typename F>
using conditional_t = typename conditional<B, T, F>::type;
6.2 C++17 的 std::void_t
template<typename...>
using void_t = void;
// 简化成员检测
template<typename T, typename = void>
struct has_value_type : std::false_type {};
template<typename T>
struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type {};
第七章:实战练习
7.1 实现一个完整的类型萃取工具
// 综合示例:实现一个智能的指针包装器
namespace my_type_traits {
// 基础类型萃取
template<typename T> struct is_const : std::false_type {};
template<typename T> struct is_const<const T> : std::true_type {};
template<typename T> struct is_pointer : std::false_type {};
template<typename T> struct is_pointer<T*> : std::true_type {};
// 移除引用
template<typename T> struct remove_reference { typedef T type; };
template<typename T> struct remove_reference<T&> { typedef T type; };
template<typename T> struct remove_reference<T&&> { typedef T type; };
// 条件选择
template<bool B, typename T, typename F>
struct conditional { typedef F type; };
template<typename T, typename F>
struct conditional<true, T, F> { typedef T type; };
// 成员检测
template<typename T>
struct has_pointer_member {
private:
template<typename U> static std::true_type test(typename U::pointer*);
template<typename> static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(nullptr))::value;
};
// 智能指针类型选择
template<typename T, typename Deleter>
struct smart_pointer_type {
typedef typename conditional<
has_pointer_member<Deleter>::value,
typename Deleter::pointer,
T*
>::type type;
};
} // namespace my_type_traits
// 使用示例
int main() {
// 测试类型萃取
static_assert(my_type_traits::is_const<const int>::value, "Should be const");
static_assert(!my_type_traits::is_const<int>::value, "Should not be const");
// 测试智能指针类型选择
struct MyDeleter {
typedef int* pointer;
};
typedef my_type_traits::smart_pointer_type<int, MyDeleter>::type ptr_type1; // int*
typedef my_type_traits::smart_pointer_type<int, std::default_delete<int>>::type ptr_type2; // int*
return 0;
}
总结
类型萃取的核心思想:
- 编译时计算:所有操作都在编译期完成
- 模板特化:通过特化实现条件逻辑
- SFINAE:利用编译错误处理实现类型检测
- 零开销:运行时没有任何额外开销
掌握类型萃取,你就掌握了 C++ 模板元编程的精髓!
参考资料
- 《C++ Templates: The Complete Guide》
- 《C++ Template Metaprogramming》
- LLVM libc++ 源码
- C++ 标准库
<type_traits>头文件