当你无法重构老代码,还能用 STL 吗?

一、背景

用上 STL 后,代码就像搭积木一样,又快又好。算法就像精密的零件,数据在优雅的容器中流动。直到有一天,你遇到一份古老的代码,把你拖回原始 for 循环的泥潭。

旧代码的接口,让 STL 的算法施展不开。该如何把旧代码关进 STL 的笼子里。本文来告诉你如何让 STL 算法与那些“不听话”的老代码共舞。

简单说,就是如何让 STL 算法,能把结果输出到不兼容 STL 的旧结构里。

二、一个简单的例子

为了方便理解,简化一下问题:

假设有一堆输入数据,放在 std::vector<Input> inputs 里。我们需要对每个输入,应用一个函数 Output f(Input const& input),然后把结果保存到一个叫 legacyRepository 的老式结构中。这个结构有个添加数据的函数 void addInRepository(Output const& value, LegacyRepository& legacyRepository),但它不是 STL 容器,用起来很麻烦。

理想情况下,可以直接用 std::transformstd::back_inserter 把结果保存到 std::vector 里。但如果没法修改 legacyRepository,该怎么办呢?

// 如果可以用 std::vector,事情就简单了
std::transform(begin(inputs), end(inputs), std::back_inserter(repository), f); 

三、模仿 std::back_inserter

std::back_inserter 给了灵感。它把数据插入到 std::vector 中,本质上是一个输出迭代器 std::back_insert_iterator。关键在于它的 operator=,它负责真正把数据放到容器里:

// (理论代码,用于说明 std::back_inserter 的工作方式)
back_insert_iterator& operator=(T const& value) {
    container_.push_back(value);
    return *this;
}

它能工作是因为 operator* 并没有返回容器的元素,而是把控制权留给了迭代器自己。operator++ 啥也不干。

这种方法在有 push_back 的容器上很好用。但能不能把它推广到其他接口上呢?

3.1、定制插入的利器

于是,自定义迭代器: custom_insert_iterator。它不依赖于特定的容器,而是接受一个自定义的函数(或者函数对象),来替代 push_back

template<typename OutputInsertFunction>
class custom_insert_iterator {
public:
    using iterator_category = std::output_iterator_tag;
    explicit custom_insert_iterator(OutputInsertFunction insertFunction) : insertFunction_(insertFunction) {}
    custom_insert_iterator& operator++(){ return *this; }
    custom_insert_iterator& operator*(){ return *this; }
    template<typename T>
    custom_insert_iterator& operator=(T const& value) {
        insertFunction_(value);
        return *this;
    }
private:
    OutputInsertFunction insertFunction_;
};

为了使用更方便,写一个辅助函数 custom_inserter

template <typename OutputInsertFunction>
custom_insert_iterator<OutputInsertFunction> custom_inserter(OutputInsertFunction insertFunction) {
    return custom_insert_iterator<OutputInsertFunction>(insertFunction);
}

现在,可以这样用它了:

// 简单粗暴版
std::copy(begin(inputs), end(inputs), 
    custom_inserter([&legacyRepository](Output const& value){ addInRepository(value, legacyRepository); }));

// 优雅版
auto insertInRepository(LegacyRepository& legacyRepository) {
    return [&legacyRepository](Output const& value) {
        addInRepository(value, legacyRepository);
    };
}

std::transform(begin(inputs), end(inputs), custom_inserter(insertInRepository(legacyRepository)));

3.2、更简单的选择?

如果只是简单的循环,直接用 for 循环更方便,比如这样:

// 简单的例子,用 for 循环更简单
for (const auto& input: inputs) addInRepository(f(input), lecgacyRepository);

但是,当需要用到更复杂的 STL 算法时,custom_inserter 的优势就体现出来了,比如:

// 当使用复杂的 STL 算法时,custom_inserter 的优势就体现出来了
std::set_difference(begin(inputs1), end(inputs1),
                    begin(inputs2), end(inputs2),
                    custom_inserter(insertInRepository(legacyRepository)));

四、新旧之争

这并没有减少遗留代码,只是增加了一个非标准的组件。那么,这样做值得吗?

这就需要权衡了。如果能彻底替换遗留代码,用上 STL 兼容的容器,那当然是最好的选择。但如果做不到,或者成本太高,那么我们就要在放弃 STL 算法,或者使用 custom_inserter 之间做出选择。虽然 custom_inserter 不是标准组件,有一定的间接性,但它可以让你在老代码中,也能享受到 STL 的便利。

这里贴一下完整代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator> // for std::output_iterator_tag

// 假设的输入输出类型和旧代码结构
struct Input {
    int value;
};

struct Output {
    int result;
};

struct LegacyRepository {
    // 老代码仓库,不兼容 STL
    void store(const Output& output) {
        std::cout << "Storing result: " << output.result << std::endl;
    }
};

// 模拟一个对 Input 进行处理的函数
Output processInput(const Input& input) {
    return {input.value * 2};
}

// 向 LegacyRepository 添加数据的函数 (需要适配的函数)
void addInRepository(const Output& value, LegacyRepository& legacyRepository) {
    legacyRepository.store(value); // 调用旧代码的存储函数
}

// 自定义插入迭代器
template<typename OutputInsertFunction>
class custom_insert_iterator {
public:
    using iterator_category = std::output_iterator_tag; // 标记为输出迭代器

    // 构造函数,接收一个函数对象
    explicit custom_insert_iterator(OutputInsertFunction insertFunction) : insertFunction_(insertFunction) {}

    // 迭代器操作符(不做任何实际操作,仅为了满足迭代器接口)
    custom_insert_iterator& operator++() { return *this; }
    custom_insert_iterator& operator*() { return *this; }

    // 赋值操作符,实际进行插入操作
    template<typename T>
    custom_insert_iterator& operator=(const T& value) {
        insertFunction_(value); // 调用用户提供的插入函数
        return *this;
    }

private:
    OutputInsertFunction insertFunction_; // 存储插入函数
};

// 辅助函数,用于创建 custom_insert_iterator,避免显式指定模板参数
template <typename OutputInsertFunction>
auto custom_inserter(OutputInsertFunction insertFunction) { // 使用 auto 推导返回类型
    return custom_insert_iterator<OutputInsertFunction>(insertFunction);
}

// 示例函数,返回一个 Lambda,封装了向 LegacyRepository 插入数据的逻辑
auto insertInRepository(LegacyRepository& legacyRepository) {
    return [&legacyRepository](const Output& value) {
        addInRepository(value, legacyRepository); // 实际调用旧代码的添加函数
    };
}

int main() {
    std::vector<Input> inputs = {{1}, {2}, {3}, {4}, {5}};
    LegacyRepository repository;

    std::cout << "Using std::transform with custom_inserter:" << std::endl;

    // 使用 std::transform 和 custom_inserter 将数据处理后插入 LegacyRepository
    std::transform(inputs.begin(), inputs.end(),
                   custom_inserter(insertInRepository(repository)), // 使用 Lambda 表达式封装插入逻辑
                   processInput); // 输入数据并处理

    // 或者使用 std::copy,需要先处理数据
    std::cout << "\nUsing std::copy with custom_inserter (after processing):" << std::endl;
    std::vector<Output> processedOutputs;
    std::transform(inputs.begin(), inputs.end(), std::back_inserter(processedOutputs), processInput);

    std::copy(processedOutputs.begin(), processedOutputs.end(), custom_inserter(insertInRepository(repository)));
    
    // 示例:与 std::set_difference 结合使用
    std::vector<Input> inputs2 = {{3}, {5}, {7}};
    std::vector<Input> differenceResult;

    std::set_difference(inputs.begin(), inputs.end(), inputs2.begin(), inputs2.end(), std::back_inserter(differenceResult),
                         [](const Input& a, const Input& b) { return a.value < b.value; });

    std::cout << "\nUsing std::set_difference with custom_inserter:" << std::endl;
    std::transform(differenceResult.begin(), differenceResult.end(), custom_inserter(insertInRepository(repository)), processInput);

    return 0;
}

五、总结

自定义 custom_insert_iterator 类允许 STL 算法直接向老代码的存储函数写入数据,而无需修改现有代码结构。通过 custom_inserter 辅助函数和 Lambda 表达式,简化了迭代器的创建和使用,实现了 STL 算法与老代码的无缝集成。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lion 莱恩呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值