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

如何编写通用相等比较函数:优先用std::cmp_equal,失败回退operator==

通用相等检查函数的跨编译器兼容实现

我需要实现一个通用相等检查函数,优先使用std::cmp_equal(因其特性),对std::cmp_equal不支持的类型(如bool或自定义类型)则回退使用operator==。原本以为用requires表达式可轻松实现,但发现该写法在libc中有效,在libstdc中却触发static_assert错误。

代码示例:

#include <utility>

template <typename T, typename U>
auto generic_equal(T t, U u) {
    if constexpr (requires { std::cmp_equal(t, u); }) {
        return std::cmp_equal(t, u);
    } else {
        return t == u;
    }
}

struct A {
    friend bool operator==(A,A) = default;
};

int main() {
    generic_equal(false, false);
    generic_equal(-1, 5);
    generic_equal(A{}, A{});
}

核心问题:如何让编译器在requires阶段就判断std::cmp_equal是否可实例化,而非在调用时才报错?


解决方案

问题根源在于libstdc++对std::cmp_equal的实现逻辑:当传入不支持的类型时,它会直接触发static_assert,而非让SFINAE机制生效。以下两种方法可以解决这个问题:

方法一:通过辅助模板利用SFINAE筛选合法调用

编写一个辅助模板,仅当std::cmp_equal可合法实例化时才启用对应的重载,否则匹配到兜底重载:

#include <utility>
#include <type_traits>

namespace detail {
template <typename T, typename U>
auto try_cmp_equal(T t, U u) -> decltype(std::cmp_equal(t, u)) {
    return std::cmp_equal(t, u);
}

// 兜底重载,当std::cmp_equal不可用时匹配此版本
template <typename...>
std::false_type try_cmp_equal(...);
}

template <typename T, typename U>
auto generic_equal(T t, U u) {
    if constexpr (std::is_same_v<decltype(detail::try_cmp_equal(t, u)), bool>) {
        return detail::try_cmp_equal(t, u);
    } else {
        return t == u;
    }
}

struct A {
    friend bool operator==(A,A) = default;
};

int main() {
    generic_equal(false, false);
    generic_equal(-1, 5);
    generic_equal(A{}, A{});
}

方法二:改进requires表达式避免触发static_assert

在requires中使用std::declval生成类型占位符,同时明确检查返回类型是否可转换为bool,这样编译器只会检查std::cmp_equal的签名合法性,不会实例化函数体从而避免触发static_assert:

#include <utility>
#include <type_traits>

template <typename T, typename U>
auto generic_equal(T t, U u) {
    if constexpr (requires { 
        { std::cmp_equal(std::declval<T>(), std::declval<U>()) } -> std::convertible_to<bool>; 
    }) {
        return std::cmp_equal(t, u);
    } else {
        return t == u;
    }
}

struct A {
    friend bool operator==(A,A) = default;
};

int main() {
    generic_equal(false, false);
    generic_equal(-1, 5);
    generic_equal(A{}, A{});
}

原理说明

  • 方法一借助SFINAE:当std::cmp_equal不可用时,detail::try_cmp_equal的第一个重载会因实例化失败被排除,转而匹配返回std::false_type的兜底重载,从而正确回退到operator==。
  • 方法二优化requires检查:使用std::declval避免传入真实参数触发函数体实例化,同时通过返回类型约束确保std::cmp_equal的调用是合法的,让libstdc++的static_assert不会被触发。

内容的提问来源于stack exchange,提问作者Kyle Markley

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 06:42:52