如何实现is_comparable trait以正确检查含不可比较value_type的std::map
问题背景
你定义了is_comparable类型特性用于检测类型是否支持==运算符,同时实现了Wrapper类,其set方法会依据is_comparable的结果决定是否执行相等判断逻辑。但当使用std::map<Key, std::any>作为Wrapper的模板参数时,is_comparable错误地判定该map是可比较类型,导致编译时触发std::any的相等判断代码,出现错误:
错误C2676:二元运算符'==':'const _Ty2'未定义该运算符,也无可转换为预定义运算符接受类型的转换(_Ty2=std::any)
你尝试为std::map特化is_comparable但未生效,需要解决这个问题。
原因分析
- 默认is_comparable的检测缺陷:如果你的
is_comparable仅检测operator==的声明是否存在,而非实际可调用性,就会误判std::map。因为std::map本身声明了operator==,但该运算符的实例化依赖于其value_type的operator==——当value_type是std::any(无默认==)时,map的operator==实际无法编译通过,但你的检测逻辑没有识别到这一点。 - 特化未生效的原因:如果你的
std::map特化没有覆盖所有模板参数(比如忽略了比较器、分配器的默认参数),编译器会将其视为与标准std::map模板不匹配的特化,从而不生效。
解决方案
方案1:修复is_comparable的检测逻辑(推荐)
重新实现is_comparable,使其真正检测operator==的可调用性,而非仅声明。利用C++17的std::void_t实现SFINAE检测:
#include <type_traits> template<typename T, typename = void> struct is_comparable : std::false_type {}; template<typename T> struct is_comparable<T, std::void_t<decltype(std::declval<const T&>() == std::declval<const T&>())>> : std::true_type {}; // 辅助变量模板 template<typename T> constexpr bool is_comparable_v = is_comparable<T>::value;
这个实现会尝试实例化const T& == const T&,如果实例化失败(比如map的value_type不可比较),则is_comparable会返回false,从而避免误判。
方案2:正确特化is_comparable针对std::map的版本
如果你坚持使用原有的is_comparable基础实现,需要为std::map编写完整的特化,覆盖所有模板参数的默认值:
#include <map> // 让std::map的可比较性依赖于其value_type的可比较性 template<typename Key, typename T, typename Compare, typename Allocator> struct is_comparable<std::map<Key, T, Compare, Allocator>> : is_comparable<T> {};
这样当std::map的value_type(比如std::any)不可比较时,is_comparable<std::map<...>>会继承is_comparable<T>的false结果,从而正确跳过相等判断逻辑。
验证
使用上述任意方案后,Wrapper<std::map<Key, std::any>>的set方法会因为is_comparable_v为false而跳过相等判断代码,避免触发std::any的operator==调用,解决编译错误。
内容的提问来源于stack exchange,提问作者Undefined

