一、Type Traits 的本质
#include <type_traits>
// is_pointer 的底层实现(简化版)
template <typename T>
struct is_pointer_impl : false_type {}; // 默认不是指针
template <typename T>
struct is_pointer_impl<T*> : true_type {}; // T* 是指针
// 使用
static_assert(is_pointer_impl<int>::value == false);
static_assert(is_pointer_impl<int*>::value == true);
true_type 和 false_type 是什么?
struct true_type { static constexpr bool value = true; };
struct false_type { static constexpr bool value = false; };
就这么简单——type traits 就是编译期的布尔值,通过模板特化在编译期确定。
二、SFINAE 的精髓
SFINAE(Substitution Failure Is Not An Error)—— 替换失败不是错误。
// 对于有 size() 成员的类型启用
template <typename T>
auto get_size(const T& x) -> decltype(x.size()) {
return x.size();
}
// 对于没有 size() 但有 length() 的类型启用
template <typename T>
auto get_size(const T& x) -> decltype(x.length()) {
return x.length();
}
// 对于没有 size() 也没有 length() 的类型启用
int get_size(...) {
return 0;
}
// 使用
vector<int> v(10);
string s = "hello";
int n = 42;
get_size(v); // 第一个版本:v.size()
get_size(s); // 第一个版本:s.size()
get_size(n); // 第三个版本:... 匹配
编译器的视角:
get_size(v): 尝试替换 T=vector<int> → x.size() 合法 → 选第一个版本
get_size(n): 尝试替换 T=int → x.size() 非法 ❌ → SFINAE 跳过此版本
尝试替换 T=int → x.length() 非法 ❌ → SFINAE 跳过此版本
尝试第三个版本(... 最不匹配,但没得选了)
三、std::enable_if 的更常用写法
// C++11/14 风格
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
half(T x) {
return x / 2;
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
half(T x) {
return x / 2.0;
}
// C++17 用 if constexpr 更干净
template <typename T>
T half(T x) {
if constexpr (std::is_integral_v<T>) {
return x / 2;
} else if constexpr (std::is_floating_point_v<T>) {
return x / 2.0;
}
}
面试高频:"if constexpr 和普通 if 的区别?"
- 普通
if:两个分支都编译,即使不执行。如果分支里有不合法代码,编译报错。 if constexpr:只有匹配的分支被编译,不匹配的分支被编译器丢弃(discarded)。可以放对当前类型不合法的代码。