如何实现可同时适配序列容器与关联容器的通用C++函数
通用跨容器has_val函数实现方案
原生无依赖最佳实现(推荐)
核心原理
通过编译期类型trait检测容器是否存在符合要求的find成员函数,根据检测结果选择最优查询逻辑,无任何运行时开销。
首先定义成员函数检测trait:
#include <type_traits> #include <algorithm> #include <iterator> // C++17及以上可用,C++11可替换为自定义void_t实现 template <typename, typename = void> struct has_member_find : std::false_type {}; template <typename C> struct has_member_find<C, std::void_t< decltype(std::declval<const C&>().find(std::declval<const typename C::key_type&>())) >> : std::is_same< typename C::const_iterator, decltype(std::declval<const C&>().find(std::declval<const typename C::key_type&>())) > {}; template <typename C> constexpr bool has_member_find_v = has_member_find<C>::value;
C++17及以上版本实现(最简写法)
用if constexpr实现编译期分支选择:
template<class C, class V = typename C::key_type> bool has_val(const C& c, const V& v) { if constexpr (has_member_find_v<C>) { // 匹配关联容器,调用成员find,复杂度符合容器特性 return c.find(v) != c.end(); } else { // 匹配序列容器,调用标准库find return std::find(c.begin(), c.end(), v) != c.end(); } }
C++11/14兼容实现
用SFINAE重载实现分支选择:
template<class C, class V> typename std::enable_if<has_member_find<C>::value, bool>::type has_val(const C& c, const V& v) { return c.find(v) != c.end(); } template<class C, class V> typename std::enable_if<!has_member_find<C>::value, bool>::type has_val(const C& c, const V& v) { return std::find(c.begin(), c.end(), v) != c.end(); }
基于现有Folly宏的实现方案
如果要使用你环境中的FOLLY_CREATE_HAS_MEMBER_FN_TRAITS宏,只需要先调用宏生成检测trait,替换上面自定义的trait即可:
// 生成find成员函数检测trait FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_member_find, find); // 检测条件写为:has_member_find<const C, typename C::const_iterator(const typename C::key_type&)>::value
你之前调用失败大概率是签名匹配错误:注意find是const成员函数,所以第一个模板参数要传const C,函数签名要严格匹配返回值为const_iterator、参数为const key_type&的形式。
优化建议
你原本的默认模板参数V = typename C::key_type更适合关联容器的按键查询场景,如果需要兼容序列容器按值查询的通用场景,可以把默认参数改为V = typename C::value_type,或者直接去掉默认参数让编译器自动推导传入值的类型,适配性更强。
内容的提问来源于stack exchange,提问作者yueq
相关产品推荐
相关产品推荐

