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

如何修复检测operator==存在性的SFINAE校验器使其支持std::pair

问题根源

你遇到的问题是C20之前标准库std::pair的operator==设计导致的:C11到C++17标准中,std::pair的operator==是无条件声明的非约束模板,SFINAE检测阶段只会检查表达式x == x的声明合法性,不会深入到函数模板实例化阶段校验内部逻辑,因此哪怕pair的元素类型不支持==运算,你的校验器也会误判pair支持operator==。

解决方案

方案一:兼容C++11及以上版本,对std::pair做特化处理

新增pair类型判断辅助模板,对has_eq做偏特化,递归校验pair的两个元素类型是否都支持operator==,如果需要支持std::tuple也可以按照相同逻辑扩展:

// 新增辅助模板,判断是否是std::pair特化
template <typename T>
struct is_pair : std::false_type {};
template <typename T1, typename T2>
struct is_pair<std::pair<T1, T2>> : std::true_type {};

// 对has_eq做偏特化,pair类型需同时满足两个元素都支持==
template <typename T1, typename T2>
struct has_eq<std::pair<T1, T2>> : std::integral_constant<bool, has_eq<T1>::value && has_eq<T2>::value> {};

把这段代码加在STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS宏定义之后即可,原来的static_assert(!has_eq<PAIR_>::value)就会正常通过。

方案二:C++20及以上版本,使用concept通用检测

C++20标准为标准库所有容器的比较运算符增加了约束,同时引入了concept语法,无需特殊处理pair类型就能得到正确的检测结果:

template <typename T>
concept has_eq = requires(const T& a, const T& b) {
    { a == b } -> std::convertible_to<bool>;
};

// 使用方式和原来一致
static_assert(!has_eq<SimpleClassWithoutComparisonOperators>);
static_assert(!has_eq<std::pair<SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators>>);

修正后完整可运行代码

#include <iostream>
#include <functional>
#include <utility>
#include <type_traits>

using namespace std;

namespace
{
  using namespace std;

  struct substitution_failure {};

  template < typename T > struct substitution_succeeded:true_type {};
  template <> struct substitution_succeeded <substitution_failure >:false_type {};


#define STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS(NAME, XTEST)                                                                              \
    namespace Private_ {                                                                                                                          \
        template <typename T>                                                                                                                     \
        struct NAME##_result_impl {                                                                                                               \
            template <typename X>                                                                                                                 \
            static auto                                                     check (const X& x) -> decltype (XTEST);                               \
            static substitution_failure check (...);                                                          \
            using type = decltype (check (declval<T> ()));                                                                                        \
        };                                                                                                                                        \
    }                                                                                                                                             \
    template <typename T>                                                                                                                         \
    using NAME##_result = typename Private_::NAME##_result_impl<T>::type;                                                                         \
    template <typename T, typename = void>                                                                                                        \
    struct has_##NAME : integral_constant<bool, not is_same<NAME##_result<T>, substitution_failure>::value> { \
    };                                                                                                                                            \
    template <typename ITERABLE>                                                                                                                  \
    constexpr bool Has##NAME##_v = has_##NAME<ITERABLE>::value;


  STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS (eq, (x == x));
  STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS (equal_to, (static_cast<bool> (std::equal_to<X>{}(x, x))));

  // ========== 新增代码开始 ==========
  template <typename T>
  struct is_pair : std::false_type {};
  template <typename T1, typename T2>
  struct is_pair<std::pair<T1, T2>> : std::true_type {};

  template <typename T1, typename T2>
  struct has_eq<std::pair<T1, T2>> : std::integral_constant<bool, has_eq<T1>::value && has_eq<T2>::value> {};
  // ========== 新增代码结束 ==========

  class SimpleClassWithoutComparisonOperators
  {
  public:
    SimpleClassWithoutComparisonOperators (size_t v) : fValue(v) {}
    SimpleClassWithoutComparisonOperators (const SimpleClassWithoutComparisonOperators& f) = default;
    ~SimpleClassWithoutComparisonOperators () = default;

    size_t GetValue () const { return fValue; }
    static size_t GetTotalLiveCount () { return sTotalLiveObjects; }

    SimpleClassWithoutComparisonOperators operator+ (const SimpleClassWithoutComparisonOperators& rhs) const
    {
      return SimpleClassWithoutComparisonOperators (fValue + rhs.fValue);
    }

  private:
    size_t fValue;
    int fConstructed = 0;
    static size_t sTotalLiveObjects;
  };

  size_t SimpleClassWithoutComparisonOperators::sTotalLiveObjects = 0;

  static_assert (!has_eq < SimpleClassWithoutComparisonOperators >::value, "SimpleClass shouldn't have ==");

  using PAIR_ = std::pair < SimpleClassWithoutComparisonOperators, SimpleClassWithoutComparisonOperators >;
  static_assert (!has_eq < PAIR_ >::value, "Pair of SimpleClass shouldn't have =="); // 现在可以正常通过

}

int main ()
{
  cout << "检测全部通过" << endl;
  return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 14:45:02