目录

一、问题:如何同时处理指针和迭代器?

二、iterator_traits 的实现原理

三、自定义 traits 类:获取容器的元素类型

四、类型函数:编译期计算类型

示例1:移除 const

示例2:移除引用

示例3:条件选择(编译期 if)

五、constexpr 与 traits 的关系

传统 traits:编译期值

C++17 变量模板简化

constexpr 函数可以替代简单的 traits

六、策略类(Policy Classes)

七、完整例子:自定义 iterator_traits

八、常见错误

1. 忘记特化导致指针类型无法使用

2. 在 traits 中使用运行时条件

3. 混淆 typename 位置

九、这一篇的收获


一、问题:如何同时处理指针和迭代器?

cpp

// 想写一个 advance 函数,让迭代器前进 n 步
template <typename Iter>
void myAdvance(Iter& it, int n) {
    // 如果是随机访问迭代器:it += n
    // 如果是单向迭代器:循环 n 次 ++it
    // 如何区分?
}

STL 的迭代器分为不同类型:

  • input_iterator_tag(只读单向)

  • forward_iterator_tag(单向读写)

  • bidirectional_iterator_tag(双向)

  • random_access_iterator_tag(随机访问)

Traits 方案:通过 iterator_traits 获取迭代器的类型信息,编译期选择算法。

cpp

#include <iterator>

template <typename Iter>
void myAdvance(Iter& it, int n) {
    using category = typename std::iterator_traits<Iter>::iterator_category;
    myAdvanceImpl(it, n, category());  // 根据迭代器类型重载
}

// 随机访问迭代器
template <typename Iter>
void myAdvanceImpl(Iter& it, int n, std::random_access_iterator_tag) {
    it += n;
}

// 双向迭代器
template <typename Iter>
void myAdvanceImpl(Iter& it, int n, std::bidirectional_iterator_tag) {
    if (n > 0) while (n--) ++it;
    else while (n++) --it;
}

// 单向迭代器
template <typename Iter>
void myAdvanceImpl(Iter& it, int n, std::forward_iterator_tag) {
    while (n--) ++it;
}

二、iterator_traits 的实现原理

iterator_traits 是一个类模板,用于提取迭代器的属性。

cpp

// 泛型版本:适用于标准迭代器
template <typename Iter>
struct iterator_traits {
    using iterator_category = typename Iter::iterator_category;
    using value_type = typename Iter::value_type;
    using difference_type = typename Iter::difference_type;
    using pointer = typename Iter::pointer;
    using reference = typename Iter::reference;
};

// 特化版本:适用于普通指针(T*)
template <typename T>
struct iterator_traits<T*> {
    using iterator_category = random_access_iterator_tag;
    using value_type = T;
    using difference_type = ptrdiff_t;
    using pointer = T*;
    using reference = T&;
};

// 特化版本:适用于 const 指针(const T*)
template <typename T>
struct iterator_traits<const T*> {
    using iterator_category = random_access_iterator_tag;
    using value_type = T;  // 注意:const T* 的 value_type 是 T,不是 const T
    using difference_type = ptrdiff_t;
    using pointer = const T*;
    using reference = const T&;
};

核心思想

  • 对于标准迭代器,直接使用迭代器内部定义的 iterator_category

  • 对于普通指针,提供特化版本,让指针也能“伪装”成随机访问迭代器

  • STL 算法通过 iterator_traits 获取信息,与容器解耦


三、自定义 traits 类:获取容器的元素类型

假设我们要写一个通用函数,获取容器的元素类型:

cpp

// 方案1:直接使用 value_type(要求容器符合 STL 规范)
template <typename Container>
struct ElementType {
    using type = typename Container::value_type;
};

// 方案2:支持数组
template <typename T, size_t N>
struct ElementType<T[N]> {
    using type = T;
};

// 方案3:支持裸指针(视为单元素)
template <typename T>
struct ElementType<T*> {
    using type = T;
};

// 使用
template <typename Container>
void process(const Container& c) {
    typename ElementType<Container>::type x;  // 编译期得到元素类型
    cout << "元素类型: " << typeid(x).name() << endl;
}

更完整的版本(类似 std::iterator_traits):

cpp

#include <vector>
#include <list>
#include <array>
#include <typeinfo>
#include <iostream>
using namespace std;

// 主模板:假设有 value_type
template <typename T>
struct ValueTypeOf {
    using type = typename T::value_type;
};

// 特化:数组
template <typename T, size_t N>
struct ValueTypeOf<T[N]> {
    using type = T;
};

// 特化:普通指针
template <typename T>
struct ValueTypeOf<T*> {
    using type = T;
};

// 辅助 using 别名(C++11)
template <typename T>
using ValueTypeOf_t = typename ValueTypeOf<T>::type;

// 通用打印函数
template <typename Container>
void printFirst(const Container& c) {
    using ElementType = ValueTypeOf_t<Container>;
    // 注意:这里简化了,实际需要检查容器是否为空
    cout << typeid(ElementType).name() << endl;
}

int main() {
    vector<int> v;
    list<double> l;
    array<string, 5> a;
    int arr[10];
    int* p = arr;
    
    printFirst(v);  // int
    printFirst(l);  // double
    printFirst(a);  // string
    printFirst(arr); // int
    printFirst(p);   // int
    
    return 0;
}

四、类型函数:编译期计算类型

Traits 的核心是类型函数——输入一个类型,输出另一个类型或值。

示例1:移除 const

cpp

template <typename T>
struct RemoveConst {
    using type = T;
};

template <typename T>
struct RemoveConst<const T> {
    using type = T;
};

template <typename T>
using RemoveConst_t = typename RemoveConst<T>::type;

// 使用
RemoveConst_t<const int> x = 42;  // x 是 int

示例2:移除引用

cpp

template <typename T>
struct RemoveReference {
    using type = T;
};

template <typename T>
struct RemoveReference<T&> {
    using type = T;
};

template <typename T>
struct RemoveReference<T&&> {
    using type = T;
};

// 使用
RemoveReference_t<int&> x = 10;   // x 是 int

示例3:条件选择(编译期 if)

cpp

template <bool Cond, typename TrueType, typename FalseType>
struct Conditional {
    using type = TrueType;
};

template <typename TrueType, typename FalseType>
struct Conditional<false, TrueType, FalseType> {
    using type = FalseType;
};

// 使用
Conditional_t<(sizeof(int) > sizeof(char)), int, char> x;
// 如果 int 大于 char,x 是 int;否则是 char

C++11 标准库已经提供了这些:<type_traits> 头文件。

cpp

#include <type_traits>

using T1 = std::remove_const<const int>::type;        // int
using T2 = std::remove_reference<int&>::type;         // int
using T3 = std::conditional<true, int, double>::type; // int
static_assert(std::is_same<T1, int>::value, "相同");

五、constexpr 与 traits 的关系

C++11 引入的 constexpr 可以计算编译期值,部分替代 traits 的“值萃取”。

传统 traits:编译期值

cpp

// 判断是否为指针(值萃取)
template <typename T>
struct IsPointer {
    static constexpr bool value = false;
};

template <typename T>
struct IsPointer<T*> {
    static constexpr bool value = true;
};

// 使用
if constexpr (IsPointer<int*>::value) {
    cout << "是指针" << endl;
}

C++17 变量模板简化

cpp

template <typename T>
inline constexpr bool is_pointer_v = IsPointer<T>::value;

// 使用
if constexpr (is_pointer_v<int*>) { ... }

constexpr 函数可以替代简单的 traits

cpp

// 编译期求阶乘
constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

int arr[factorial(5)];  // 编译期计算,arr[120]

// 但 constexpr 函数不能处理类型操作(如判断是否为指针)
// 类型操作仍然需要 traits
能力 traits constexpr
类型变换(如移除 const)
编译期值计算
类型判断(如 is_pointer)
编译期分支选择 ✅(特化) ✅(if constexpr)

六、策略类(Policy Classes)

Traits 用于描述“是什么”,策略类用于描述“怎么做”。

cpp

// 策略类:定义排序方式
struct Ascending {
    template <typename T>
    static bool compare(const T& a, const T& b) {
        return a < b;
    }
};

struct Descending {
    template <typename T>
    static bool compare(const T& a, const T& b) {
        return a > b;
    }
};

// 算法类:接受策略作为模板参数
template <typename T, typename Policy = Ascending>
class Sorter {
public:
    static void sort(std::vector<T>& vec) {
        for (size_t i = 0; i < vec.size(); i++) {
            for (size_t j = i + 1; j < vec.size(); j++) {
                if (Policy::compare(vec[j], vec[i])) {
                    std::swap(vec[i], vec[j]);
                }
            }
        }
    }
};

// 使用
vector<int> v = {3, 1, 4, 1, 5};
Sorter<int, Ascending>::sort(v);   // 升序
Sorter<int, Descending>::sort(v);  // 降序

Traits vs Policy

维度 Traits Policy
关注点 对象“是什么” 算法“怎么做”
典型用途 iterator_traits 排序策略、分配器
获取方式 通过类型萃取 作为模板参数传入

七、完整例子:自定义 iterator_traits

cpp

#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <type_traits>
using namespace std;

// 自定义的 iterator_traits(简化版)
template <typename Iter>
struct MyIteratorTraits {
    using value_type = typename Iter::value_type;
    using difference_type = typename Iter::difference_type;
    using iterator_category = typename Iter::iterator_category;
};

// 特化:普通指针
template <typename T>
struct MyIteratorTraits<T*> {
    using value_type = T;
    using difference_type = ptrdiff_t;
    using iterator_category = random_access_iterator_tag;
};

// 打印迭代器类型名称的辅助函数
template <typename Iter>
void printIteratorInfo(const Iter& it) {
    using traits = MyIteratorTraits<Iter>;
    using category = typename traits::iterator_category;
    
    cout << "迭代器类型: ";
    if (is_same<category, random_access_iterator_tag>::value) {
        cout << "随机访问";
    } else if (is_same<category, bidirectional_iterator_tag>::value) {
        cout << "双向";
    } else if (is_same<category, forward_iterator_tag>::value) {
        cout << "单向";
    } else {
        cout << "输入";
    }
    cout << endl;
}

// 使用 traits 的 distance 实现
template <typename Iter>
typename MyIteratorTraits<Iter>::difference_type
myDistance(Iter first, Iter last) {
    using category = typename MyIteratorTraits<Iter>::iterator_category;
    return myDistanceImpl(first, last, category());
}

// 随机访问迭代器
template <typename Iter>
typename MyIteratorTraits<Iter>::difference_type
myDistanceImpl(Iter first, Iter last, random_access_iterator_tag) {
    cout << "[随机访问版本] ";
    return last - first;
}

// 非随机访问迭代器
template <typename Iter>
typename MyIteratorTraits<Iter>::difference_type
myDistanceImpl(Iter first, Iter last, forward_iterator_tag) {
    cout << "[循环版本] ";
    typename MyIteratorTraits<Iter>::difference_type n = 0;
    while (first != last) {
        ++first;
        ++n;
    }
    return n;
}

int main() {
    // vector 是随机访问迭代器
    vector<int> vec = {1, 2, 3, 4, 5};
    cout << "vector distance: " << myDistance(vec.begin(), vec.end()) << endl;
    
    // list 是双向迭代器(退化到循环版本)
    list<int> lst = {1, 2, 3, 4, 5};
    cout << "list distance: " << myDistance(lst.begin(), lst.end()) << endl;
    
    // 普通指针也是随机访问
    int arr[] = {1, 2, 3, 4, 5};
    cout << "array distance: " << myDistance(arr, arr + 5) << endl;
    
    return 0;
}

输出:

text

[随机访问版本] vector distance: 5
[循环版本] list distance: 5
[随机访问版本] array distance: 5

八、常见错误

1. 忘记特化导致指针类型无法使用

cpp

// ❌ 泛型版本要求 T::value_type,指针没有
template <typename T>
void process(T t) {
    typename T::value_type x;  // 传入 int* 时编译错误
}

2. 在 traits 中使用运行时条件

traits 必须在编译期确定,不能依赖运行时数据。

3. 混淆 typename 位置

cpp

// ❌ 错误
typename T::iterator it;
// ✅ 正确
typename T::iterator it;
// 但变量声明前要加 typename,类型定义时也要注意

九、这一篇的收获

你现在应该理解:

  • Traits(萃取):编译期获取类型信息的机制,通过模板特化实现

  • iterator_traits:让算法统一处理迭代器和指针,STL 的核心设计

  • 类型函数:输入类型,输出类型(如 remove_const)或值(如 is_pointer

  • constexpr:可替代部分值萃取,但不能替代类型变换

  • 策略类:把算法中的可变行为作为模板参数,与 traits 互补

💡 小作业:实现一个 is_same 类型萃取,编译期判断两个类型是否相同。实现 enable_if 的简化版本,用于 SFINAE。


下一篇预告:第46篇《CRTP(奇异递归模板模式):静态多态的妙用》——派生类把自己作为模板参数传给基类,实现编译期多态,避免虚函数开销。CRTP 是 C++ 中一个巧妙的惯用法,广泛用于静态多态、对象计数、混入类等场景。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐