You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为嵌套容器基于已有谓词构建新谓词的优化方案问询

private:
std::set<T, TComparePredicate> _internalSet;
};

但实际场景更复杂,需要在`std::set`中存储`std::pair<T, V>`类型的数据,代码框架如下:
```cpp
template <typename T, typename V, typename TComparePredicate = std::less<T>>
class MyContainer
{
public:
    MyContainer(const TComparePredicate& predicate = TComparePredicate()) : _internalSet(???) { ... }
    ...
private:
    std::set<std::pair<T, V>, ???> _internalSet;
};

我需要仅依据T类型的值对std::set排序,目前的解决方案是创建一个转发仿函数,将调用委托给原谓词:

template <typename T, typename V, typename Predicate>
struct PairForwardingFunctor
{
    PairForwardingFunctor(const Predicate& predicate = Predicate()) : _predicate(predicate) { }
    bool operator()(const std::pair<T, V>& lhs, const std::pair<T, V>& rhs) { return _predicate(lhs.first, rhs.first); }

private:
    Predicate _predicate;
};

但我担心该方案存在无法优化的间接调用层,想询问是否有更优的实现方式?

解决方案

1. 匿名lambda直接作为比较谓词(C++11及以上)

可以直接在std::set的模板参数中使用lambda,配合传入的自定义谓词,无需额外定义转发结构体:

template <typename T, typename V, typename TComparePredicate = std::less<T>>
class MyContainer
{
public:
    MyContainer(const TComparePredicate& predicate = TComparePredicate())
        : _internalSet([pred = predicate](const std::pair<T, V>& lhs, const std::pair<T, V>& rhs) {
            return pred(lhs.first, rhs.first);
        })
    { }
    // 容器的其他成员函数...
private:
    // 推导lambda的类型作为比较器类型
    using PairComparer = decltype([](const std::pair<T, V>&, const std::pair<T, V>&) { return false; });
    std::set<std::pair<T, V>, PairComparer> _internalSet;
};

注意:C11中lambda无法作为模板参数的默认值,若要支持默认构造,可调整比较器的类型定义;C20及以上可借助std::move_only_function进一步提升灵活性。

2. C++20的std::bind_front简化绑定

如果使用C++20,std::bind_front可以直接将谓词与pair::first的提取逻辑绑定,无需手动编写转发代码:

#include <functional>

template <typename T, typename V, typename TComparePredicate = std::less<T>>
class MyContainer
{
public:
    MyContainer(const TComparePredicate& predicate = TComparePredicate())
        : _internalSet([pred = std::move(predicate)](const auto& lhs, const auto& rhs) {
            return std::invoke(pred, lhs.first, rhs.first);
        })
    { }
    // 容器的其他成员函数...
private:
    using PairComparer = decltype(
        [](const std::pair<T, V>&, const std::pair<T, V>&) { return false; }
    );
    std::set<std::pair<T, V>, PairComparer> _internalSet;
};

std::invoke能兼容函数指针、仿函数、lambda等各种可调用对象,灵活性极强。也可以直接用std::bind_front构造比较器:

// 构造函数中的初始化改为:
_internalSet(std::bind_front(predicate, &std::pair<T, V>::first, &std::pair<T, V>::first))
// 对应的PairComparer类型推导:
using PairComparer = decltype(
    std::bind_front(TComparePredicate{}, &std::pair<T, V>::first, &std::pair<T, V>::first)
);

3. 原方案的性能无需过度担忧

现代编译器(如GCC、Clang、MSVC)在开启优化(例如-O2)时,会将你的转发仿函数逻辑完全内联,不会产生额外的间接调用开销。由于PairForwardingFunctor是模板化的,编译器能明确知晓要调用的谓词类型,轻松消除转发层的性能损耗。

若仍有疑虑,可查看编译器生成的汇编代码,或通过性能测试对比不同方案的执行效率——实际场景中原方案的性能与更简洁的写法几乎无差异。


内容的提问来源于stack exchange,提问作者Andrea Bocco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:23:16