如何在C++17中为std::variant内的double重写比较运算符
简洁实现std::variant的自定义比较逻辑(针对double的epsilon比较)
可以利用C++17的std::visit结合模板特化或constexpr if,在不堆砌冗余代码的前提下,为std::variant中的double类型单独实现基于epsilon的比较,同时保留其他类型的默认比较行为。
方案一:使用std::visit + constexpr if
这种方式逻辑集中,适合类型较少的场景:
首先定义epsilon常量(可根据需求调整精度):
constexpr double DOUBLE_EPSILON = 1e-9;
然后实现通用的比较函数:
#include <variant> #include <wchar.h> #include <cmath> #include <stdexcept> enum class RELATION { EQ, NE, GT, LE, LT, GE }; bool compare_variants(const std::variant<std::wstring, double, int>& lhs, const std::variant<std::wstring, double, int>& rhs, RELATION relation) { if (lhs.index() != rhs.index()) { throw std::logic_error("无法比较不同类型的variant"); } return std::visit([relation](const auto& a, const auto& b) -> bool { using T = std::decay_t<decltype(a)>; if constexpr (std::is_same_v<T, double>) { // 对double类型用epsilon处理相等/不等,排序逻辑保留默认浮点比较 switch (relation) { case RELATION::EQ: return std::fabs(a - b) < DOUBLE_EPSILON; case RELATION::NE: return std::fabs(a - b) >= DOUBLE_EPSILON; case RELATION::GT: return a > b; case RELATION::LE: return a <= b; case RELATION::LT: return a < b; case RELATION::GE: return a >= b; default: return false; } } else { // 其他类型直接使用默认比较运算符 switch (relation) { case RELATION::EQ: return a == b; case RELATION::NE: return a != b; case RELATION::GT: return a > b; case RELATION::LE: return a <= b; case RELATION::LT: return a < b; case RELATION::GE: return a >= b; default: return false; } } }, lhs, rhs); }
方案二:模板特化分离比较逻辑
如果后续需要扩展更多类型的自定义比较,模板特化的方式更具扩展性:
先定义通用比较模板,再特化double版本:
constexpr double DOUBLE_EPSILON = 1e-9; template <typename T> bool compare(const T& a, const T& b, RELATION relation) { switch (relation) { case RELATION::EQ: return a == b; case RELATION::NE: return a != b; case RELATION::GT: return a > b; case RELATION::LE: return a <= b; case RELATION::LT: return a < b; case RELATION::GE: return a >= b; default: return false; } } // 特化double类型的比较逻辑 template <> bool compare<double>(const double& a, const double& b, RELATION relation) { switch (relation) { case RELATION::EQ: return std::fabs(a - b) < DOUBLE_EPSILON; case RELATION::NE: return std::fabs(a - b) >= DOUBLE_EPSILON; // 排序逻辑复用通用版本的实现 default: return compare<int>(a, b, relation); } }
然后在std::visit中调用这个模板函数:
bool compare_variants(const std::variant<std::wstring, double, int>& lhs, const std::variant<std::wstring, double, int>& rhs, RELATION relation) { if (lhs.index() != rhs.index()) { throw std::logic_error("无法比较不同类型的variant"); } return std::visit([relation](const auto& a, const auto& b) -> bool { return compare(a, b, relation); }, lhs, rhs); }
替换原有代码
原有的类方法可以简化为:
auto CachedValue = ...; // std::variant<std::wstring, double, int> const auto& FirstValue = ...; // std::variant<std::wstring, double, int> bool Result = compare_variants(CachedValue, FirstValue, m_Relation); return Result;
方案优势
- 代码结构清晰:将自定义比较逻辑集中在一处,避免原代码中重复的类型判断与分支
- 可读性强:通过
std::visit自动匹配variant的实际类型,无需手动维护索引与类型的映射 - 扩展性好:后续新增类型或修改比较规则时,只需调整模板特化或
constexpr if分支即可 - 符合需求:类型不匹配时直接抛出异常,而非返回无效结果
内容的提问来源于stack exchange,提问作者Ilia
相关产品推荐
相关产品推荐

