文档目录

一、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)。可以放对当前类型不合法的代码。