为嵌套容器基于已有谓词构建新谓词的优化方案问询
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
相关产品推荐
相关产品推荐

